{
  "formato": "plantilla-vault/portable",
  "version_formato": 1,
  "plantilla": {
    "slug_origen": "gimnasios-fitness",
    "version_exportada": 29,
    "nombre": "Gimnasios y Fitness",
    "icono": "🏋️",
    "categoria": "fitness",
    "descripcion": "Plantilla completa de agentes de IA para gimnasios y centros de fitness: membresías, clases, entrenamiento personal, nutrición, onboarding, retención, challenges y referidos.",
    "indice": "",
    "notas_version": "1 campos modificados",
    "estructura": [],
    "modulos": [
      {
        "id": "membership-enquiry",
        "carpeta": "membership-enquiry-agent",
        "nombre": "Membership Enquiry Agent",
        "icono": "💳",
        "descripcion": "Responder preguntas sobre planes de membresía, precios y beneficios",
        "submodulos": [],
        "categoria": "general",
        "estructura": [
          {
            "id": "membership-enquiry-agent",
            "etiqueta": "",
            "es_plantilla": true,
            "nombre": "membership-enquiry-agent",
            "subcarpetas": [],
            "notas": [
              {
                "id": "membership-enquiry-agent/flujo.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "flujo.md",
                "contenido": "# Flujo de ejecución — Membership Enquiry Agent\n\nEste archivo documenta el flujo fijo de ejecución del agente. Cada consulta de membresía sigue estos 5 pasos **en orden**, sin pasos condicionales ni bifurcaciones.\n\n---\n\n## Paso 1 — Recepción de la consulta o evento disparador\n\nEl agente recibe la consulta o solicitud de información sobre membresías de un contacto (prospecto o miembro actual) a través de cualquier canal configurado.\n\n**Acción del agente:**\n1. Recibir el evento disparador `consulta_membresia`.\n2. Extraer los campos de entrada definidos en `variables-entrada.md`.\n3. Registrar la marca de tiempo de recepción en `fecha_hora_recepcion`.\n4. Generar un `consulta_id` único para el caso.\n5. Verificar que la consulta no esté vacía y tenga al menos 3 caracteres.\n6. Clasificar la consulta en una de las categorías definidas.\n\n**Ejemplo de entrada:**\n\n```json\n{\n  \"evento\": \"consulta_membresia\",\n  \"canal_origen\": \"whatsapp\",\n  \"numero_remitente\": \"+52 55 9876 5432\",\n  \"mensaje\": \"Hola, ¿cuánto cuesta la membresía premium? Quiero saber qué incluye\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"gimnasio_telefono\": \"+52 55 4321 8765\"\n}\n```\n\n---\n\n## Paso 2 — Procesamiento de los datos según las reglas fijas\n\nEl agente procesa la información recibida aplicando las reglas de negocio para generar una respuesta precisa y completa.\n\n**Acción del agente:**\n1. Clasificar la consulta en `categoria_consulta` según la tabla de categorías de `prompt-sistema.md`.\n2. Extraer los datos relevantes del mensaje:\n   - `nombre_contacto`: nombre del contacto (si está disponible en el perfil o el mensaje).\n   - `plan_consultado`: plan específico mencionado (si aplica).\n   - `servicio_consultado`: servicio o beneficio mencionado (si aplica).\n3. Buscar la información correspondiente en los datos de planes y servicios del gimnasio (inyectados desde configuración).\n4. Validar que la información solicitada esté disponible:\n   - Si la información está disponible → proceder al paso 3.\n   - Si la información **no** está disponible → marcar `informacion_no_disponible: true` y proceder al paso 3 con respuesta parcial + escalamiento (ver `reglas-escalamiento.md` → C2).\n5. Determinar la acción siguiente sugerida según la categoría:\n   - `PRECIOS_PLANES` → ofrecer comparativa de planes y opción de clase de prueba.\n   - `BENEFICIOS` → detallar beneficios del plan y ofrecer visita guiada.\n   - `ESTADO_CUENTA` → mostrar datos de vigencia y próximo cobro.\n   - `CAMBIO_PLAN` → informar procedimiento y ofrecer asesoría con humano.\n   - `CANCELACION` → informar política de cancelación y verificar con humano.\n   - `RENOVACION` → informar opciones de renovación y posibles beneficios.\n   - `CLASES_GRUPALES` → mostrar horarios disponibles y ofrecer inscripción.\n   - `PRUEBA_GRATUITA` → reservar clase de prueba y confirmar detalles.\n   - `OTRO` → responder con información general y ofrecer asesoría.\n\n**Ejemplo de procesamiento:**\n\n```json\n{\n  \"consulta_id\": \"ENQ-20260823-103000-001\",\n  \"categoria_consulta\": \"PRECIOS_PLANES\",\n  \"nombre_contacto\": \"Ana García\",\n  \"plan_consultado\": \"Premium\",\n  \"servicio_consultado\": null,\n  \"informacion_no_disponible\": false,\n  \"accion_sugerida\": \"comparativa_planes_mas_clase_prueba\"\n}\n```\n\n---\n\n## Paso 3 — Generación de la salida específica requerida\n\nEl agente genera el mensaje de respuesta al contacto y, si corresponde, la notificación al equipo interno. Cada salida sigue el formato definido en `formato-salida.md`.\n\n**Acción del agente:**\n1. Generar el mensaje de respuesta al contacto usando la plantilla correspondiente de `formato-salida.md` → sección 1, según la `categoria_consulta`.\n2. Insertar los datos reales del contacto, los planes, servicios y precios en los campos de la plantilla.\n3. Si `informacion_no_disponible` es `true`, incluir el mensaje de escalamiento según `formato-salida.md` → sección 3.\n4. Si la categoría es `CANCELACION` o `CAMBIO_PLAN`, generar también la notificación al equipo interno según `formato-salida.md` → sección 2.\n5. Registrar el `estado_salida` como `generada`.\n6. Validar que el mensaje generado no exceda **500 caracteres** por párrafo.\n\n**Plantilla de respuesta general (whatsapp/sms):**\n\n```\n¡Hola {{nombre_contacto}}! 💪 Gracias por tu interés en {{gimnasio_nombre}}.\n\n{{texto_respuesta_personalizado}}\n\n{{accion_sugerida_texto}}\n\n¿Te gustaría que te reserve una clase de prueba gratuita? 🏋️\n— {{gimnasio_nombre}}\n```\n\n**Plantilla de respuesta general (email):**\n\n**Asunto:** Tu consulta en {{gimnasio_nombre}} — {{categoria_consulta_nombre}}\n\n```\nHola {{nombre_contacto}},\n\nGracias por contactar a {{gimnasio_nombre}}.\n\n{{texto_respuesta_personalizado}}\n\n{{accion_sugerida_texto}}\n\nSi tienes más preguntas, no dudes en escribirnos o llámanos al {{gimnasio_telefono}}.\n\n¡Te esperamos! 💪\n— {{gimnasio_nombre}}\n```\n\n**Ejemplo renderizado (whatsapp — consulta de precios):**\n\n> ¡Hola Ana García! 💪 Gracias por tu interés en FitZone Premium.\n>\n> Estos son nuestros planes de membresía:\n>\n> 🔹 **Básico** — $399 MXN/mes — Acceso a gimnasio y zona de cardio\n> 🔹 **Premium** — $699 MXN/mes — Todo lo del Básico + clases grupales y sauna\n> 🔹 **Elite** — $999 MXN/mes — Todo lo del Premium + entrenamiento personal 1 vez/semana\n>\n> ¿Te gustaría que te reserve una clase de prueba gratuita? 🏋️\n> — FitZone Premium\n\n---\n\n## Paso 4 — Entrega o envío de la salida al destinatario correspondiente\n\nEl agente envía la respuesta generada al contacto por el canal correspondiente y, si aplica, la notificación al equipo interno.\n\n**Acción del agente:**\n1. Verificar que el `canal_origen` sea un canal válido (`whatsapp`, `sms`, `email`, `app`, `web`).\n2. Enviar la respuesta al contacto por el `canal_origen`.\n3. Si el envío falla, reintentar una sola vez después de 30 segundos.\n4. Si el segundo intento falla, registrar el error y activar escalamiento (ver `reglas-escalamiento.md` → C3.1).\n5. Si se generó notificación al equipo interno (paso 3), enviarla por el canal configurado (`canal_notificacion_equipo`).\n6. Registrar la marca de tiempo de envío en `fecha_hora_envio`.\n7. Actualizar `estado_salida` de `generada` a `enviada`.\n\n**Tabla de canales y formatos:**\n\n| Canal | Formato de mensaje | Prioridad |\n|---|---|---|\n| `whatsapp` | Texto con emojis y formato markdown | Normal |\n| `sms` | Texto plano, máximo 160 caracteres por mensaje | Normal |\n| `email` | Texto con asunto y cuerpo separados | Normal |\n| `app` | Notificación push con texto corto + enlace | Normal |\n| `web` | Mensaje en widget de chat | Normal |\n\n---\n\n## Paso 5 — Registro de la acción en el historial del contacto o caso\n\nEl agente registra toda la interacción en el sistema para auditoría y seguimiento.\n\n**Acción del agente:**\n1. Generar el registro completo de la consulta con el formato de `formato-salida.md` → sección 4 (registro JSON).\n2. Registrar la marca de tiempo de cierre en `fecha_hora_cierre`.\n3. Calcular `duracion_proceso_segundos` = `fecha_hora_cierre` - `fecha_hora_recepcion`.\n4. Actualizar `estado_consulta` a `completada` o `escalamiento` según corresponda.\n5. Guardar el registro en el sistema de historial de contactos.\n6. Si el contacto tiene un perfil existente, asociar el `consulta_id` al historial del contacto.\n\n**Tabla de resultados posibles:**\n\n| Resultado | Descripción | Próxima acción del sistema |\n|---|---|---|\n| `informacion_entregada` | Se respondió con la información solicitada | Cerrar caso. No requiere seguimiento. |\n| `clase_prueba_reservada` | Se reservó una clase de prueba para el contacto | Cerrar caso. Seguimiento en 24 horas para confirmar asistencia. |\n| `escalamiento` | Se escaló a humano por condición de `reglas-escalamiento.md` | Esperar resolución del humano. Plazo según categoría de escalamiento. |\n| `consulta_incompleta` | El mensaje del contacto no contiene información suficiente | Cerrar caso. El contacto reintentará con más detalles. |\n| `envio_fallido` | No se pudo entregar la respuesta al contacto | Escalar a humano (C3.1). Registro de error guardado. |\n"
              },
              {
                "id": "membership-enquiry-agent/formato-salida.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "formato-salida.md",
                "contenido": "# Formato de salida — Membership Enquiry Agent\n\nEste archivo define la estructura exacta de los mensajes y registros que el agente debe producir. No se permite desviarse de estas plantillas.\n\n---\n\n## 1. Mensaje de respuesta al contacto — Por categoría de consulta\n\n### 1.1 Consulta de precios y planes (`PRECIOS_PLANES`)\n\n#### Plantilla (whatsapp/sms)\n\n```\n¡Hola {{nombre_contacto}}! 💪 Gracias por tu interés en {{gimnasio_nombre}}.\n\nEstos son nuestros planes de membresía:\n\n🔹 **Básico** — ${{precio_basico}} {{moneda}}/mes — {{beneficios_basico_resumen}}\n🔹 **Premium** — ${{precio_premium}} {{moneda}}/mes — {{beneficios_premium_resumen}}\n🔹 **Elite** — ${{precio_elite}} {{moneda}}/mes — {{beneficios_elite_resumen}}\n\n{{nota_descuento_anual}}\n\n¿Te gustaría que te reserve una clase de prueba gratuita? 🏋️\n— {{gimnasio_nombre}}\n```\n\n**Ejemplo renderizado:**\n\n> ¡Hola Ana García! 💪 Gracias por tu interés en FitZone Premium.\n>\n> Estos son nuestros planes de membresía:\n>\n> 🔹 **Básico** — $399 MXN/mes — Acceso a gimnasio y zona de cardio\n> 🔹 **Premium** — $699 MXN/mes — Todo lo del Básico + clases grupales y sauna\n> 🔹 **Elite** — $999 MXN/mes — Todo lo del Premium + entrenamiento personal 1 vez/semana\n>\n> 💡 Pago anual con 10% de descuento.\n>\n> ¿Te gustaría que te reserve una clase de prueba gratuita? 🏋️\n> — FitZone Premium\n\n---\n\n### 1.2 Consulta de beneficios (`BENEFICIOS`)\n\n#### Plantilla (whatsapp/sms)\n\n```\n¡Hola {{nombre_contacto}}! 💪 Aquí tienes los detalles del plan **{{plan_nombre}}** de {{gimnasio_nombre}}:\n\n✅ **Incluido:**\n{{lista_beneficios_incluidos}}\n\n❌ **No incluido:**\n{{lista_beneficios_no_incluidos}}\n\n💰 **Precio:** ${{precio_plan}} {{moneda}}/mes\n\n¿Quieres conocernos? Agenda una visita guiada sin compromiso:\n📍 {{gimnasio_direccion}}\n— {{gimnasio_nombre}}\n```\n\n**Ejemplo renderizado:**\n\n> ¡Hola Ana García! 💪 Aquí tienes los detalles del plan **Premium** de FitZone Premium:\n>\n> ✅ **Incluido:**\n> • Acceso a zona de gimnasio\n> • Acceso a zona de cardio\n> • Clases grupales ilimitadas\n> • Sauna y vapor\n> • Estacionamiento incluido\n>\n> ❌ **No incluido:**\n> • Entrenamiento personal\n>\n> 💰 **Precio:** $699 MXN/mes\n>\n> ¿Quieres conocernos? Agenda una visita guiada sin compromiso:\n> 📍 Av. Constituyentes 1230, Col. Las Américas, CDMX\n> — FitZone Premium\n\n---\n\n### 1.3 Estado de cuenta (`ESTADO_CUENTA`)\n\n#### Plantilla (whatsapp/sms)\n\n```\n¡Hola {{nombre_contacto}}! 🏋️ Te comparto el estado de tu membresía en {{gimnasio_nombre}}:\n\n📋 **Plan actual:** {{plan_actual_nombre}}\n📅 **Vigencia hasta:** {{fecha_fin_membresia_formateada}}\n💳 **Próximo cobro:** {{fecha_proximo_cobro_formateada}} — ${{monto_proximo_cobro}} {{moneda}}\n\n¿Necesitas algo más? Estamos para ayudarte.\n— {{gimnasio_nombre}}\n```\n\n**Ejemplo renderizado:**\n\n> ¡Hola Ana García! 🏋️ Te comparto el estado de tu membresía en FitZone Premium:\n>\n> 📋 **Plan actual:** Premium\n> 📅 **Vigencia hasta:** 15 de diciembre de 2026\n> 💳 **Próximo cobro:** 15 de septiembre de 2026 — $699 MXN\n>\n> ¿Necesitas algo más? Estamos para ayudarte.\n> — FitZone Premium\n\n---\n\n### 1.4 Cambio de plan (`CAMBIO_PLAN`)\n\n#### Plantilla (whatsapp/sms)\n\n```\n¡Hola {{nombre_contacto}}! 💪 Para cambiar tu plan de **{{plan_actual_nombre}}** a **{{plan_nuevo_nombre}}** en {{gimnasio_nombre}}, necesitas:\n\n1. Hablar con nuestro equipo de atención al cliente.\n2. El cambio se aplicará desde tu próximo ciclo de facturación.\n3. La diferencia de precio se ajusta de forma proporcional.\n\n📍 Puedes visitarnos en {{gimnasio_direccion}}\n📞 O llámanos al {{gimnasio_telefono}}\n\n¿Te gustaría que un asesor te contacte para procesar el cambio?\n— {{gimnasio_nombre}}\n```\n\n**Ejemplo renderizado:**\n\n> ¡Hola Ana García! 💪 Para cambiar tu plan de **Básico** a **Premium** en FitZone Premium, necesitas:\n>\n> 1. Hablar con nuestro equipo de atención al cliente.\n> 2. El cambio se aplicará desde tu próximo ciclo de facturación.\n> 3. La diferencia de precio se ajusta de forma proporcional.\n>\n> 📍 Puedes visitarnos en Av. Constituyentes 1230, Col. Las Américas, CDMX\n> 📞 O llámanos al +52 55 4321 8765\n>\n> ¿Te gustaría que un asesor te contacte para procesar el cambio?\n> — FitZone Premium\n\n---\n\n### 1.5 Cancelación (`CANCELACION`)\n\n#### Plantilla (whatsapp/sms)\n\n```\nHola {{nombre_contacto}}. 🏋️ Entendemos tu decisión.\n\nPara cancelar tu membresía de **{{plan_actual_nombre}}** en {{gimnasio_nombre}}:\n\n📋 **Política de cancelación:**\n{{texto_politica_cancelacion}}\n\n📞 Un asesor se pondrá en contacto contigo para procesar la cancelación y resolver cualquier duda.\n\nSi prefieres, llámanos al {{gimnasio_telefono}}.\n— {{gimnasio_nombre}}\n```\n\n**Ejemplo renderizado:**\n\n> Hola Ana García. 🏋️ Entendemos tu decisión.\n>\n> Para cancelar tu membresía de **Premium** en FitZone Premium:\n>\n> 📋 **Política de cancelación:**\n> Las cancelaciones deben realizarse con al menos 15 días de anticipación. Se aplica una penalización del 10% sobre el monto restante del contrato.\n>\n> 📞 Un asesor se pondrá en contacto contigo para procesar la cancelación y resolver cualquier duda.\n>\n> Si prefieres, llámanos al +52 55 4321 8765.\n> — FitZone Premium\n\n---\n\n### 1.6 Renovación (`RENOVACION`)\n\n#### Plantilla (whatsapp/sms)\n\n```\n¡Hola {{nombre_contacto}}! 💪 Tu membresía de **{{plan_actual_nombre}}** en {{gimnasio_nombre}} vence el **{{fecha_fin_membresia_formateada}}**.\n\n{{texto_opciones_renovacion}}\n\n¿Deseas renovar o tienes alguna pregunta?\n— {{gimnasio_nombre}}\n```\n\n**Ejemplo renderizado:**\n\n> ¡Hola Ana García! 💪 Tu membresía de **Premium** en FitZone Premium vence el **15 de diciembre de 2026**.\n>\n> 🔄 **Renovación automática:** Se renovará por otro año el {{fecha_fin_membresia_formateada}} con un 5% de descuento.\n> 💡 **Renovación anticipada:** Si renuevas antes del {{fecha_limite_renovacion}}, obtienes 10% de descuento.\n>\n> ¿Deseas renovar o tienes alguna pregunta?\n> — FitZone Premium\n\n---\n\n### 1.7 Consulta de clases grupales (`CLASES_GRUPALES`)\n\n#### Plantilla (whatsapp/sms)\n\n```\n¡Hola {{nombre_contacto}}! 💪 Aquí tienes los horarios de **{{clase_nombre}}** en {{gimnasio_nombre}}:\n\n📅 **Horarios disponibles:**\n{{lista_horarios_clase}}\n\n👥 **Cupo máximo:** {{cupo_maximo}} personas\n💰 **Precio por clase:** ${{precio_clase}} {{moneda}} (incluido en planes Premium y Elite)\n\n{{texto_reserva_clase}}\n\n— {{gimnasio_nombre}}\n```\n\n**Ejemplo renderizado:**\n\n> ¡Hola Ana García! 💪 Aquí tienes los horarios de **Spinning** en FitZone Premium:\n>\n> 📅 **Horarios disponibles:**\n> • Lunes: 07:00–08:00\n> • Miércoles: 07:00–08:00\n> • Viernes: 18:00–19:00\n>\n> 👥 **Cupo máximo:** 25 personas\n> 💰 **Precio por clase:** $80 MXN (incluido en planes Premium y Elite)\n>\n> 📲 Reserva tu lugar respondiendo con el día y hora que prefieras.\n>\n> — FitZone Premium\n\n---\n\n### 1.8 Clase de prueba (`PRUEBA_GRATUITA`)\n\n#### Plantilla (whatsapp/sms)\n\n```\n¡Hola {{nombre_contacto}}! 🎉 ¡Excelente decisión!\n\nTu clase de prueba en **{{gimnasio_nombre}}** queda así:\n\n📅 **Fecha:** {{fecha_clase_prueba_formateada}}\n🕐 **Hora:** {{hora_clase_prueba}}\n📍 **Lugar:** {{gimnasio_direccion}}\n🏋️ **Clase sugerida:** {{clase_sugerida}}\n\n💡 **Tips para tu primera visita:**\n• Llega 10 minutos antes.\n• Trae ropa deportiva y tenis cómodos.\n• Trae una botella de agua.\n\n¿Confirmas tu asistencia? Responde \"Sí\" para confirmar.\n— {{gimnasio_nombre}}\n```\n\n**Ejemplo renderizado:**\n\n> ¡Hola Ana García! 🎉 ¡Excelente decisión!\n>\n> Tu clase de prueba en **FitZone Premium** queda así:\n>\n> 📅 **Fecha:** 25 de agosto de 2026\n> 🕐 **Hora:** 10:00\n> 📍 **Lugar:** Av. Constituyentes 1230, Col. Las Américas, CDMX\n> 🏋️ **Clase sugerida:** Spinning (07:00–08:00) o Yoga (09:00–10:00)\n>\n> 💡 **Tips para tu primera visita:**\n> • Llega 10 minutos antes.\n> • Trae ropa deportiva y tenis cómodos.\n> • Trae una botella de agua.\n>\n> ¿Confirmas tu asistencia? Responde \"Sí\" para confirmar.\n> — FitZone Premium\n\n---\n\n### 1.9 Consulta general (`OTRO`)\n\n#### Plantilla (whatsapp/sms)\n\n```\n¡Hola {{nombre_contacto}}! 💪 Gracias por escribirnos a {{gimnasio_nombre}}.\n\n{{texto_respuesta_general}}\n\n📍 **Dirección:** {{gimnasio_direccion}}\n📞 **Teléfono:** {{gimnasio_telefono}}\n🕐 **Horario:** {{horario_atencion_inicio}}–{{horario_atencion_fin}}\n\n¿En qué más podemos ayudarte?\n— {{gimnasio_nombre}}\n```\n\n**Ejemplo renderizado:**\n\n> ¡Hola Ana García! 💪 Gracias por escribirnos a FitZone Premium.\n>\n> Te compartimos nuestra información general:\n>\n> 📍 **Dirección:** Av. Constituyentes 1230, Col. Las Américas, CDMX\n> 📞 **Teléfono:** +52 55 4321 8765\n> 🕐 **Horario:** 06:00–22:00\n>\n> ¿En qué más podemos ayudarte?\n> — FitZone Premium\n\n---\n\n## 2. Notificación al equipo interno — Consulta de cancelación o cambio de plan\n\n### Plantilla (whatsapp_interno)\n\n```\n📋 CONSULTA DE {{categoria_notificacion}} — {{gimnasio_nombre}}\n==========================================\n\n**Contacto:** {{nombre_contacto}}\n**Teléfono:** {{telefono_contacto}}\n**Canal:** {{canal_origen}}\n\n📋 **Plan actual:** {{plan_actual_nombre}}\n📅 **Vigencia hasta:** {{fecha_fin_membresia_formateada}}\n❓ **Motivo:** {{motivo_consulta}}\n\n💬 **Mensaje original:** \"{{mensaje_consulta}}\"\n\n🔗 Registro: {{consulta_id}}\n⏱️ Plazo de respuesta: {{plazo_respuesta_horas}} horas\n```\n\n### Plantilla (email_interno)\n\n**Asunto:** {{categoria_notificacion}} — {{nombre_contacto}} — {{gimnasio_nombre}}\n\n```\nConsulta de {{categoria_notificacion}} recibida en {{gimnasio_nombre}}.\n\nContacto: {{nombre_contacto}}\nTeléfono: {{telefono_contacto}}\nCanal: {{canal_origen}}\n\nPlan actual: {{plan_actual_nombre}}\nVigencia hasta: {{fecha_fin_membresia_formateada}}\nMotivo: {{motivo_consulta}}\n\nMensaje original: \"{{mensaje_consulta}}\"\n\nRegistro: {{consulta_id}}\nPlazo de respuesta: {{plazo_respuesta_horas}} horas\n```\n\n**Ejemplo renderizado (whatsapp_interno):**\n\n> 📋 CONSULTA DE CANCELACIÓN — FitZone Premium\n> ==========================================\n>\n> **Contacto:** Ana García López\n> **Teléfono:** +52 55 9876 5432\n> **Canal:** whatsapp\n>\n> 📋 **Plan actual:** Premium\n> 📅 **Vigencia hasta:** 15 de diciembre de 2026\n> ❓ **Motivo:** Cliente indica que ya no puede asistir regularmente por cambio de horario laboral.\n>\n> 💬 **Mensaje original:** \"Quiero cancelar mi membresía, ya no puedo ir al gym por mi nuevo trabajo\"\n>\n> 🔗 Registro: ENQ-20260823-103000-001\n> ⏱️ Plazo de respuesta: 4 horas\n\n---\n\n## 3. Mensaje de escalamiento al contacto\n\n### Plantilla (whatsapp/sms)\n\n```\nEntendido, {{nombre_contacto}}. 💪 Voy a conectar con nuestro equipo para atender tu consulta de forma personalizada.\n\nUn asesor se pondrá en contacto contigo en las próximas **{{plazo_contacto_horas}} horas**.\n\nSi necesitas algo urgente, llámanos al **{{gimnasio_telefono}}**.\n— {{gimnasio_nombre}}\n```\n\n### Plantilla (email)\n\n**Asunto:** Tu consulta — {{gimnasio_nombre}}\n\n```\nHola {{nombre_contacto}},\n\nGracias por contactar a {{gimnasio_nombre}}.\n\nTu consulta ha sido recibida y un asesor se pondrá en contacto contigo en las próximas **{{plazo_contacto_horas}} horas** para atenderla de forma personalizada.\n\nSi necesitas algo urgente, llámanos al **{{gimnasio_telefono}}** o visítanos en **{{gimnasio_direccion}}**.\n\n¡Te esperamos! 💪\n— {{gimnasio_nombre}}\n```\n\n**Ejemplo renderizado (whatsapp):**\n\n> Entendido, Ana García. 💪 Voy a conectar con nuestro equipo para atender tu consulta de forma personalizada.\n>\n> Un asesor se pondrá en contacto contigo en las próximas **4 horas**.\n>\n> Si necesitas algo urgente, llámanos al **+52 55 4321 8765**.\n> — FitZone Premium\n\n---\n\n## 4. Registro de consulta completo (JSON)\n\nSe genera al finalizar el paso 5 del flujo para cada consulta procesada.\n\n```json\n{\n  \"registro_id\": \"ENQREG-YYYYMMDD-HHMMSS-XXX\",\n  \"consulta_id\": \"ENQ-YYYYMMDD-HHMMSS-XXX\",\n  \"evento_id\": \"string\",\n  \"contacto_id\": \"CON-YYYYMMDD-XXX\",\n  \"gimnasio_id\": \"string\",\n  \"gimnasio_nombre\": \"string\",\n  \"nombre_contacto\": \"string\",\n  \"telefono_contacto\": \"string\",\n  \"email_contacto\": \"string | null\",\n  \"canal_origen\": \"whatsapp | sms | email | app | web\",\n  \"es_miembro_actual\": true | false,\n  \"membresia_id\": \"string | null\",\n  \"plan_actual\": \"basico | premium | elite | null\",\n  \"mensaje_consulta\": \"string\",\n  \"fecha_hora_recepcion\": \"YYYY-MM-DDTHH:MM:SS\",\n  \"categoria_consulta\": \"PRECIOS_PLANES | BENEFICIOS | ESTADO_CUENTA | CAMBIO_PLAN | CANCELACION | RENOVACION | CLASES_GRUPALES | PRUEBA_GRATUITA | OTRO\",\n  \"plan_consultado\": \"string | null\",\n  \"servicio_consultado\": \"string | null\",\n  \"informacion_no_disponible\": true | false,\n  \"accion_sugerida\": \"string\",\n  \"texto_respuesta_personalizado\": \"string\",\n  \"accion_sugerida_texto\": \"string\",\n  \"fecha_hora_envio\": \"YYYY-MM-DDTHH:MM:SS | null\",\n  \"estado_salida\": \"generada | enviada | error_envio\",\n  \"resultado\": \"informacion_entregada | clase_prueba_reservada | escalamiento | consulta_incompleta | envio_fallido\",\n  \"fue_escalado\": true | false,\n  \"motivo_escalamiento\": \"string | null\",\n  \"fecha_hora_cierre\": \"YYYY-MM-DDTHH:MM:SS\",\n  \"duracion_proceso_segundos\": 0,\n  \"agente\": \"membership-enquiry-agent\"\n}\n```\n\n### Ejemplo — Consulta de precios respondida exitosamente\n\n```json\n{\n  \"registro_id\": \"ENQREG-20260823-103005-001\",\n  \"consulta_id\": \"ENQ-20260823-103000-001\",\n  \"evento_id\": \"ENQ-20260823-103000-001\",\n  \"contacto_id\": \"CON-20260823-201\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"nombre_contacto\": \"Ana García López\",\n  \"telefono_contacto\": \"+52 55 9876 5432\",\n  \"email_contacto\": null,\n  \"canal_origen\": \"whatsapp\",\n  \"es_miembro_actual\": false,\n  \"membresia_id\": null,\n  \"plan_actual\": null,\n  \"mensaje_consulta\": \"Hola, ¿cuánto cuesta la membresía premium? Quiero saber qué incluye\",\n  \"fecha_hora_recepcion\": \"2026-08-23T10:30:00\",\n  \"categoria_consulta\": \"PRECIOS_PLANES\",\n  \"plan_consultado\": \"premium\",\n  \"servicio_consultado\": null,\n  \"informacion_no_disponible\": false,\n  \"accion_sugerida\": \"comparativa_planes_mas_clase_prueba\",\n  \"texto_respuesta_personalizado\": \"Estos son nuestros planes de membresía:\\n\\n🔹 Básico — $399 MXN/mes — Acceso a gimnasio y zona de cardio\\n🔹 Premium — $699 MXN/mes — Todo lo del Básico + clases grupales y sauna\\n🔹 Elite — $999 MXN/mes — Todo lo del Premium + entrenamiento personal 1 vez/semana\",\n  \"accion_sugerida_texto\": \"¿Te gustaría que te reserve una clase de prueba gratuita? 🏋️\",\n  \"fecha_hora_envio\": \"2026-08-23T10:30:03\",\n  \"estado_salida\": \"enviada\",\n  \"resultado\": \"informacion_entregada\",\n  \"fue_escalado\": false,\n  \"motivo_escalamiento\": null,\n  \"fecha_hora_cierre\": \"2026-08-23T10:30:05\",\n  \"duracion_proceso_segundos\": 5,\n  \"agente\": \"membership-enquiry-agent\"\n}\n```\n\n### Ejemplo — Estado de cuenta de miembro activo\n\n```json\n{\n  \"registro_id\": \"ENQREG-20260823-140005-002\",\n  \"consulta_id\": \"ENQ-20260823-140000-002\",\n  \"evento_id\": \"ENQ-20260823-140000-002\",\n  \"contacto_id\": \"CON-20260115-101\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"nombre_contacto\": \"Roberto Ruiz Méndez\",\n  \"telefono_contacto\": \"+52 55 4567 8901\",\n  \"email_contacto\": \"roberto.ruiz@correo.com\",\n  \"canal_origen\": \"whatsapp\",\n  \"es_miembro_actual\": true,\n  \"membresia_id\": \"MEM-20260115-101\",\n  \"plan_actual\": \"premium\",\n  \"mensaje_consulta\": \"¿Cuándo vence mi membresía? Quiero saber cuánto falta para el próximo cobro\",\n  \"fecha_hora_recepcion\": \"2026-08-23T14:00:00\",\n  \"categoria_consulta\": \"ESTADO_CUENTA\",\n  \"plan_consultado\": null,\n  \"servicio_consultado\": null,\n  \"informacion_no_disponible\": false,\n  \"accion_sugerida\": \"mostrar_estado_cuenta\",\n  \"texto_respuesta_personalizado\": \"Plan actual: Premium\\nVigencia hasta: 15 de diciembre de 2026\\nPróximo cobro: 15 de septiembre de 2026 — $699 MXN\",\n  \"accion_sugerida_texto\": \"¿Necesitas algo más? Estamos para ayudarte.\",\n  \"fecha_hora_envio\": \"2026-08-23T14:00:02\",\n  \"estado_salida\": \"enviada\",\n  \"resultado\": \"informacion_entregada\",\n  \"fue_escalado\": false,\n  \"motivo_escalamiento\": null,\n  \"fecha_hora_cierre\": \"2026-08-23T14:00:04\",\n  \"duracion_proceso_segundos\": 4,\n  \"agente\": \"membership-enquiry-agent\"\n}\n```\n\n### Ejemplo — Consulta de cancelación (escalamiento)\n\n```json\n{\n  \"registro_id\": \"ENQREG-20260823-163005-003\",\n  \"consulta_id\": \"ENQ-20260823-163000-003\",\n  \"evento_id\": \"ENQ-20260823-163000-003\",\n  \"contacto_id\": \"CON-20260301-305\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"nombre_contacto\": \"Laura Hernández\",\n  \"telefono_contacto\": \"+52 55 1122 3344\",\n  \"email_contacto\": null,\n  \"canal_origen\": \"whatsapp\",\n  \"es_miembro_actual\": true,\n  \"membresia_id\": \"MEM-20260301-305\",\n  \"plan_actual\": \"elite\",\n  \"mensaje_consulta\": \"Quiero cancelar mi membresía, ya no puedo ir al gym por mi nuevo trabajo\",\n  \"fecha_hora_recepcion\": \"2026-08-23T16:30:00\",\n  \"categoria_consulta\": \"CANCELACION\",\n  \"plan_consultado\": null,\n  \"servicio_consultado\": null,\n  \"informacion_no_disponible\": false,\n  \"accion_sugerida\": \"informar_politica_mas_escalamiento\",\n  \"texto_respuesta_personalizado\": \"Entendemos tu decisión. Para cancelar tu membresía de Elite:\\n\\nPolítica: Las cancelaciones deben realizarse con al menos 15 días de anticipación. Se aplica una penalización del 10% sobre el monto restante del contrato.\",\n  \"accion_sugerida_texto\": \"Un asesor se pondrá en contacto contigo para procesar la cancelación.\",\n  \"fecha_hora_envio\": \"2026-08-23T16:30:03\",\n  \"estado_salida\": \"enviada\",\n  \"resultado\": \"escalamiento\",\n  \"fue_escalado\": true,\n  \"motivo_escalamiento\": \"Solicitud de cancelación de membresía. Requiere procesamiento humano para verificar política de cancelación y penalización aplicable.\",\n  \"fecha_hora_cierre\": \"2026-08-23T16:30:05\",\n  \"duracion_proceso_segundos\": 5,\n  \"agente\": \"membership-enquiry-agent\"\n}\n```\n\n---\n\n## 5. Registro de escalamiento a humano (JSON)\n\nSe genera cuando se activa una condición de escalamiento (ver `reglas-escalamiento.md`).\n\n```json\n{\n  \"registro_id\": \"ENQREG-YYYYMMDD-HHMMSS-XXX\",\n  \"consulta_id\": \"ENQ-YYYYMMDD-HHMMSS-XXX\",\n  \"evento\": \"escalamiento\",\n  \"fecha_hora_ejecucion\": \"YYYY-MM-DDTHH:MM:SS\",\n  \"agente\": \"membership-enquiry-agent\",\n  \"gimnasio_id\": \"string\",\n  \"gimnasio_nombre\": \"string\",\n  \"nombre_contacto\": \"string\",\n  \"telefono_contacto\": \"string\",\n  \"email_contacto\": \"string | null\",\n  \"canal_origen\": \"whatsapp | sms | email | app | web\",\n  \"es_miembro_actual\": true | false,\n  \"membresia_id\": \"string | null\",\n  \"plan_actual\": \"basico | premium | elite | null\",\n  \"categoria_consulta\": \"string\",\n  \"mensaje_consulta\": \"string\",\n  \"resultado\": \"escalamiento\",\n  \"detalles\": \"Motivo: {{motivo_escalamiento}}.\",\n  \"responsable_escalamiento\": \"string nombre del responsable\",\n  \"canal_escalamiento\": \"whatsapp_interno | email_interno\",\n  \"plazo_respuesta_horas\": 0,\n  \"datos_origen\": {\n    \"consulta_id\": \"{{consulta_id}}\",\n    \"nombre_contacto\": \"{{nombre_contacto}}\",\n    \"telefono_contacto\": \"{{telefono_contacto}}\",\n    \"categoria_consulta\": \"{{categoria_consulta}}\",\n    \"es_miembro_actual\": true | false,\n    \"plan_actual\": \"{{plan_actual}}\"\n  }\n}\n```\n\n### Ejemplo — Datos incompletos del contacto\n\n```json\n{\n  \"registro_id\": \"ENQREG-20260823-110010-004\",\n  \"consulta_id\": \"ENQ-20260823-110000-004\",\n  \"evento\": \"escalamiento\",\n  \"fecha_hora_ejecucion\": \"2026-08-23T11:00:10\",\n  \"agente\": \"membership-enquiry-agent\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"nombre_contacto\": \"Contacto\",\n  \"telefono_contacto\": \"+52 55 9988 7766\",\n  \"email_contacto\": null,\n  \"canal_origen\": \"whatsapp\",\n  \"es_miembro_actual\": true,\n  \"membresia_id\": null,\n  \"plan_actual\": null,\n  \"categoria_consulta\": \"ESTADO_CUENTA\",\n  \"mensaje_consulta\": \"¿Cuándo vence mi membresía?\",\n  \"resultado\": \"escalamiento\",\n  \"detalles\": \"Motivo: Miembro indica que es miembro actual pero no se proporcionó membresia_id ni plan_actual. No se puede verificar el estado de la cuenta.\",\n  \"responsable_escalamiento\": \"Carlos Mendoza\",\n  \"canal_escalamiento\": \"whatsapp_interno\",\n  \"plazo_respuesta_horas\": 4,\n  \"datos_origen\": {\n    \"consulta_id\": \"ENQ-20260823-110000-004\",\n    \"nombre_contacto\": \"Contacto\",\n    \"telefono_contacto\": \"+52 55 9988 7766\",\n    \"categoria_consulta\": \"ESTADO_CUENTA\",\n    \"es_miembro_actual\": true,\n    \"plan_actual\": null\n  }\n}\n```\n\n### Ejemplo — Envío fallido tras reintentos\n\n```json\n{\n  \"registro_id\": \"ENQREG-20260823-170035-005\",\n  \"consulta_id\": \"ENQ-20260823-170000-005\",\n  \"evento\": \"escalamiento\",\n  \"fecha_hora_ejecucion\": \"2026-08-23T17:00:35\",\n  \"agente\": \"membership-enquiry-agent\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"nombre_contacto\": \"María López\",\n  \"telefono_contacto\": \"+52 55 5566 7788\",\n  \"email_contacto\": null,\n  \"canal_origen\": \"whatsapp\",\n  \"es_miembro_actual\": false,\n  \"membresia_id\": null,\n  \"plan_actual\": null,\n  \"categoria_consulta\": \"PRUEBA_GRATUITA\",\n  \"mensaje_consulta\": \"Quiero hacer una prueba, ¿cuándo puedo ir?\",\n  \"resultado\": \"escalamiento\",\n  \"detalles\": \"Motivo: Error de envío por canal whatsapp tras 2 intentos fallidos al enviar respuesta a María López (consulta ENQ-20260823-170000-005).\",\n  \"responsable_escalamiento\": \"Carlos Mendoza\",\n  \"canal_escalamiento\": \"whatsapp_interno\",\n  \"plazo_respuesta_horas\": 2,\n  \"datos_origen\": {\n    \"consulta_id\": \"ENQ-20260823-170000-005\",\n    \"nombre_contacto\": \"María López\",\n    \"telefono_contacto\": \"+52 55 5566 7788\",\n    \"categoria_consulta\": \"PRUEBA_GRATUITA\",\n    \"es_miembro_actual\": false,\n    \"plan_actual\": null\n  }\n}\n```\n\n---\n\n## 6. Formato de fecha para mensajes al contacto\n\nLas fechas se presentan en el siguiente formato para los mensajes al contacto:\n\n| Elemento | Formato | Ejemplo |\n|---|---|---|\n| `fecha_formateada` | Día + mes + año en español | `\"23 de agosto de 2026\"` |\n\n### Tabla de meses\n\n| Mes | Formato |\n|---|---|\n| Enero | `\"enero\"` |\n| Febrero | `\"febrero\"` |\n| Marzo | `\"marzo\"` |\n| Abril | `\"abril\"` |\n| Mayo | `\"mayo\"` |\n| Junio | `\"junio\"` |\n| Julio | `\"julio\"` |\n| Agosto | `\"agosto\"` |\n| Septiembre | `\"septiembre\"` |\n| Octubre | `\"octubre\"` |\n| Noviembre | `\"noviembre\"` |\n| Diciembre | `\"diciembre\"` |\n"
              },
              {
                "id": "membership-enquiry-agent/prompt-sistema.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "prompt-sistema.md",
                "contenido": "# Prompt del sistema — Membership Enquiry Agent\n\n## Identidad\nEres **FitBot**, asistente virtual de **GymPro Fitness Center**. Tu función es gestionar consultas sobre membresías del gimnasio de forma profesional y eficiente.\n\n## Tono de comunicación\n- **Empático y motivacional**: transmitir energía positiva alineada con el mundo fitness.\n- **Directo y práctico**: ir al grano sin rodeos innecesarios.\n- **Formal-cercano**: usar trato de \"usted\" pero con calidez.\n- Evitar jerga técnica innecesaria; explicar términos fitness si el cliente lo requiere.\n\n## Flujo de ejecución\nEjecuta los siguientes pasos en orden exacto, sin omitir ni reordenar:\n\n### Paso 1 — Recepción del evento\nRecibe la información o evento disparador correspondiente a la consulta de membresía (mensaje de WhatsApp, formulario web, llamada registrada).\n\n### Paso 2 — Procesamiento de datos\nProcesa los datos según las reglas fijas definidas para este caso de uso:\n- Extraer: nombre del consultor, tipo de membresía consultada, datos de contacto.\n- Verificar que los campos obligatorios estén presentes.\n- Clasificar la consulta: información general, precios, beneficios, comparativas, proceso de inscripción.\n\n### Paso 3 — Generación de salida\nGenera la salida específica requerida:\n- Respuesta informativa con datos concretos de la membresía consultada.\n- Incluir precios, horarios, beneficios y condiciones de forma clara.\n- Usar únicamente información de la base de conocimiento oficial del gimnasio.\n\n### Paso 4 — Entrega al destinatario\nEntrega o envía esa salida al destinatario correspondiente:\n- Si es consulta del cliente → enviar respuesta por el mismo canal.\n- Si requiere seguimiento → notificar al equipo de ventas.\n\n### Paso 5 — Registro\nRegistra la acción realizada en el historial del contacto o caso:\n- Tipo de consulta atendida.\n- Membresía sobre la cual se consultó.\n- Resultado: respondida, derivada a ventas, pendiente de información adicional.\n\n## Ejemplo de ejecución\n\n**Entrada:**\n- nombre_cliente: \"Carlos Mendoza\"\n- telefono: \"+52-55-1234-5678\"\n- consulta: \"¿Cuánto cuesta la membresía Premium mensual?\"\n\n**Salida generada:**\n```\nHola Carlos 👋\n\nGracias por tu interés en GymPro. La membresía Premium mensual tiene un costo de $899 MXN e incluye:\n\n✅ Acceso ilimitado a todas las áreas\n✅ 4 clases grupales a la semana\n✅ 1 asesoría nutricional mensual\n✅ Acceso a la app GymPro\n\n¿Te gustaría agendar una visita para conocer nuestras instalaciones?\n```\n\n## Restricciones\n- No inventar precios ni promociones no autorizadas.\n- No dar información médica o de salud (escalar a profesional).\n- No procesar pagos directamente (derivar a sistema de cobro).\n- No acceder a información de otros miembros.\n- Responder en el mismo idioma del cliente.\n\n## Reglas de escalamiento\n- Si el cliente solicita información médica → escalar a nutriólogo o fisioterapeuta.\n- Si hay conflicto de precios o facturación → escalar a gerencia administrativa.\n- Si el cliente reporta emergencia médica → transferir a emergencias (911) y notificar a seguridad.\n- Si la consulta requiere acceso a datos sensibles no disponibles → escalar a administración.\n- Tiempo máximo de respuesta: 2 minutos. Si excede, escalar a equipo humano.\n"
              },
              {
                "id": "membership-enquiry-agent/reglas-escalamiento.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "reglas-escalamiento.md",
                "contenido": "# Reglas de escalamiento — Membership Enquiry Agent\n\nEste archivo define la lista cerrada y exacta de condiciones bajo las cuales el agente **debe** transferir el caso a un humano (asesor de membresías, gerente o responsable de atención al cliente). No existe discrecionalidad: si se cumple alguna condición, el escalamiento es **obligatorio**.\n\n---\n\n## Condiciones de escalamiento\n\n### C1 — Datos del contacto o membresía incompletos o inválidos\n\n| # | Condición | Acción |\n|---|---|---|\n| C1.1 | El contacto indica que es miembro actual (`es_miembro_actual: true`) pero el `membresia_id` no está disponible o tiene formato inválido | Escalamiento obligatorio. Motivo: `Miembro indica que es miembro actual pero no se proporcionó membresia_id válido. No se puede verificar el estado de la cuenta.` No se procesa la consulta. |\n| C1.2 | El contacto indica que es miembro actual pero el `plan_actual` no está disponible o no es uno de los 3 valores válidos (`basico`, `premium`, `elite`) | Escalamiento obligatorio. Motivo: `Plan actual del miembro no disponible o no reconocido ('{{plan_actual}}'). No se puede generar respuesta personalizada.` No se procesa la consulta. |\n| C1.3 | El `nombre_contacto` está vacío, es `null` o es genérico (ej. \"Contacto\", \"Sin nombre\", \"Usuario\") y la consulta requiere datos del perfil | Escalamiento obligatorio. Motivo: `Nombre del contacto no disponible o genérico. No se puede personalizar la respuesta.` No se procesa la consulta hasta confirmar identidad. |\n| C1.4 | El `telefono_contacto` tiene menos de 8 dígitos o más de 15 dígitos y el `canal_origen` es `whatsapp` o `sms` | Escalamiento obligatorio. Motivo: `Teléfono del contacto '{{telefono_contacto}}' no cumple con el formato válido (8-15 dígitos). No es posible enviar respuesta por {{canal_origen}}.` No se envía respuesta. |\n| C1.5 | El `canal_origen` es `email` pero el `email_remitente` es `null` o está vacío | Escalamiento obligatorio. Motivo: `Canal configurado como 'email' pero el contacto no tiene email registrado. No es posible enviar respuesta.` No se envía respuesta. |\n| C1.6 | El `email_remitente` está presente pero no tiene formato válido de email | Escalamiento obligatorio. Motivo: `Email del contacto '{{email_remitente}}' no tiene formato válido. No es posible enviar respuesta por email.` No se envía respuesta. |\n\n---\n\n### C2 — Información del gimnasio no disponible\n\n| # | Condición | Acción |\n|---|---|---|\n| C2.1 | `planes_disponibles` está vacío o no contiene ningún plan con `activo: true` | Escalamiento obligatorio. Motivo: `No hay planes de membresía activos configurados en el sistema. No se puede responder consultas de precios o planes.` No se envía respuesta al contacto. |\n| C2.2 | La consulta es sobre precios (`categoria_consulta: PRECIOS_PLANES`) pero el plan consultado no existe en `planes_disponibles` | Escalamiento obligatorio. Motivo: `Plan '{{plan_consultado}}' no encontrado en la lista de planes disponibles. No se puede proporcionar información de precios.` Se informa al contacto que un asesor le contactará. |\n| C2.3 | La consulta es sobre precios pero el `precio_mensual` del plan es `0`, `null` o negativo | Escalamiento obligatorio. Motivo: `Precio del plan '{{plan_consultado}}' no válido (${{precio_mensual}}). Verificar configuración de precios.` No se envía precio incorrecto al contacto. |\n| C2.4 | La consulta es sobre clases grupales (`categoria_consulta: CLASES_GRUPALES`) pero `servicios_disponibles` está vacío o no contiene servicios activos | Escalamiento obligatorio. Motivo: `No hay servicios o clases configurados en el sistema. No se puede responder consultas sobre clases grupales.` Se informa al contacto que un asesor le contactará. |\n| C2.5 | La consulta es sobre estado de cuenta (`categoria_consulta: ESTADO_CUENTA`) pero `fecha_fin_membresia` es `null` o tiene formato inválido | Escalamiento obligatorio. Motivo: `Fecha de vencimiento de membresía no disponible o con formato inválido para el miembro {{nombre_contacto}} (membresía {{membresia_id}}). No se puede informar el estado.` No se envía información incorrecta. |\n| C2.6 | La consulta es sobre renovación (`categoria_consulta: RENOVACION`) pero `fecha_fin_membresia` es `null` o anterior a la fecha actual sin que se haya procesado una renovación | Escalamiento obligatorio. Motivo: `Membresía de {{nombre_contacto}} sin fecha de vencimiento válida o vencida sin renovación procesada. Requiere revisión manual.` No se envía información incorrecta. |\n\n---\n\n### C3 — Fallo técnico de envío\n\n| # | Condición | Acción |\n|---|---|---|\n| C3.1 | El envío del mensaje de respuesta falla **2 veces consecutivas** (intento original + 1 reintento tras 30 segundos) | Escalamiento obligatorio. Motivo: `Error de envío por canal {{canal_origen}} tras 2 intentos fallidos al enviar respuesta a {{nombre_contacto}} (consulta {{consulta_id}}).` Generar registro de escalamiento. Notificar al equipo interno para contacto directo. |\n| C3.2 | El `canal_origen` no es uno de los 5 valores válidos (`whatsapp`, `sms`, `email`, `app`, `web`) | Escalamiento obligatorio. Motivo: `Canal de origen '{{canal_origen}}' no reconocido para la consulta {{consulta_id}}.` No se procesa la consulta. |\n| C3.3 | El destino del envío (número de teléfono, email o token) está vacío o es inválido | Escalamiento obligatorio. Motivo: `Destino de envío inválido o vacío para canal {{canal_origen}}.` No se envía respuesta. |\n\n---\n\n### C4 — Consulta de cancelación o cambio de plan\n\n| # | Condición | Acción |\n|---|---|---|\n| C4.1 | La consulta es de tipo `CANCELACION` | Escalamiento obligatorio. Motivo: `Solicitud de cancelación de membresía de {{nombre_contacto}} (plan {{plan_actual}}, membresía {{membresia_id}}). Requiere procesamiento humano para verificar política de cancelación, penalización aplicable y ofrecer alternativas.` Generar registro de escalamiento. Notificar al equipo interno con `formato-salida.md` → sección 2. Plazo: **4 horas**. |\n| C4.2 | La consulta es de tipo `CAMBIO_PLAN` y el contacto solicita un plan de menor precio (ej. de `elite` a `basico`) | Escalamiento obligatorio. Motivo: `Solicitud de cambio de plan de menor precio ({{plan_actual}} → {{plan_nuevo_consultado}}) de {{nombre_contacto}}. Requiere verificación de política de downgrade y posible ofrecimiento de alternativas.` Generar registro de escalamiento. Notificar al equipo interno. Plazo: **4 horas**. |\n| C4.3 | La consulta es de tipo `CAMBIO_PLAN` pero no se puede determinar el plan destino solicitado | Escalamiento obligatorio. Motivo: `Solicitud de cambio de plan sin plan destino claro de {{nombre_contacto}}. No se puede procesar sin definir el plan deseado.` Generar registro de escalamiento. Notificar al equipo interno. Plazo: **4 horas**. |\n\n---\n\n### C5 — Consulta ambigua o no clasificable\n\n| # | Condición | Acción |\n|---|---|---|\n| C5.1 | La consulta se clasifica como `OTRO` y el `mensaje_consulta` tiene menos de 10 caracteres | Escalamiento obligatorio. Motivo: `Consulta demasiado breve ({{longitud_mensaje}} caracteres) y no clasificable. No se puede determinar la intención del contacto.` No se envía respuesta genérica. Se notifica al equipo interno para contacto directo. |\n| C5.2 | La consulta se clasifica como `OTRO` pero el mensaje contiene palabras clave que sugieren una categoría diferente que no fue detectada (ej. \"factura\", \"cobro\", \"cargo no reconocido\", \"reclamo\") | Escalamiento obligatorio. Motivo: `Consulta clasificada como OTRO pero contiene keywords que sugieren tema no cubierto por el agente: '{{keywords_detectadas}}'. Requiere revisión humana.` Generar registro de escalamiento. Notificar al equipo interno. Plazo: **4 horas**. |\n\n---\n\n### C6 — Consulta de prueba gratuita sin disponibilidad\n\n| # | Condición | Acción |\n|---|---|---|\n| C6.1 | La consulta es de tipo `PRUEBA_GRATUITA` pero no hay horarios disponibles para ninguna clase en los próximos 7 días | Escalamiento obligatorio. Motivo: `Solicitud de clase de prueba sin horarios disponibles en los próximos 7 días. No se puede ofrecer alternativa automatizada.` Generar registro de escalamiento. Notificar al equipo interno para coordinar disponibilidad manual. Plazo: **2 horas**. |\n\n---\n\n## Resumen de tiempos de escalamiento\n\n| Condición | Tiempo antes de escalar | Prioridad |\n|---|---|---|\n| Membresía_id inválido (C1.1) | 0 segundos (inmediato) | Normal |\n| Plan_actual inválido (C1.2) | 0 segundos (inmediato) | Normal |\n| Nombre genérico (C1.3) | 0 segundos (inmediato) | Normal |\n| Teléfono inválido (C1.4) | 0 segundos (inmediato) | Normal |\n| Email faltante (C1.5) | 0 segundos (inmediato) | Normal |\n| Email inválido (C1.6) | 0 segundos (inmediato) | Normal |\n| Sin planes activos (C2.1) | 0 segundos (inmediato) | Normal |\n| Plan no encontrado (C2.2) | 0 segundos (inmediato) | Normal |\n| Precio inválido (C2.3) | 0 segundos (inmediato) | Normal |\n| Sin clases configuradas (C2.4) | 0 segundos (inmediato) | Normal |\n| Fecha vencimiento inválida (C2.5) | 0 segundos (inmediato) | Normal |\n| Membresía vencida sin renovar (C2.6) | 0 segundos (inmediato) | Normal |\n| Fallo envío 2 intentos (C3.1) | 0 segundos tras 1 reintento fallido | Normal |\n| Canal no reconocido (C3.2) | 0 segundos (inmediato) | Normal |\n| Destino inválido (C3.3) | 0 segundos (inmediato) | Normal |\n| Solicitud cancelación (C4.1) | 0 segundos (inmediato) | **Alta** |\n| Cambio plan downgrade (C4.2) | 0 segundos (inmediato) | Normal |\n| Cambio plan sin destino (C4.3) | 0 segundos (inmediato) | Normal |\n| Mensaje ambiguo breve (C5.1) | 0 segundos (inmediato) | Normal |\n| Tema no cubierto detectado (C5.2) | 0 segundos (inmediato) | Normal |\n| Sin disponibilidad prueba (C6.1) | 0 segundos (inmediato) | Normal |\n\n---\n\n## Plazos de respuesta del equipo interno\n\n| Tipo de escalamiento | Plazo de respuesta esperado |\n|---|---|\n| Solicitud de cancelación (C4.1) | 4 horas |\n| Cambio de plan downgrade (C4.2) | 4 horas |\n| Cambio de plan sin destino (C4.3) | 4 horas |\n| Tema no cubierto detectado (C5.2) | 4 horas |\n| Sin disponibilidad para prueba (C6.1) | 2 horas |\n| Datos del contacto incompletos (C1.1–C1.6) | 4 horas |\n| Información del gimnasio no disponible (C2.1–C2.6) | 4 horas |\n| Fallo técnico de envío (C3.1–C3.3) | 2 horas |\n| Mensaje ambiguo breve (C5.1) | 4 horas |\n\n---\n\n## Proceso de escalamiento\n\nCuando se activa cualquiera de las condiciones C1–C6:\n\n1. El agente genera el registro de escalamiento con `resultado: \"escalamiento\"` usando el formato de `formato-salida.md` → sección 5.\n2. El agente envía al contacto el mensaje de escalamiento según `formato-salida.md` → sección 3 (excepto para C1.4, C1.5, C1.6, C2.1, C3.1, C3.2, C3.3 donde no se puede enviar respuesta al contacto).\n3. El agente notifica al equipo interno con el formato de `formato-salida.md` → sección 2 (cancelación/cambio) o con los datos del registro de escalamiento.\n4. El agente actualiza `resultado` a `escalamiento` y `fue_escalado` a `true`.\n5. El agente espera la resolución del equipo interno dentro del plazo correspondiente.\n6. Si el equipo interno no responde dentro del plazo, el agente notifica al responsable con nivel de urgencia correspondiente.\n\n---\n\n## Textos base de escalamiento\n\nLos siguientes textos se usan literalmente en los registros de escalamiento. No se deben modificar ni parafrasear.\n\n| Condición | Texto del campo `detalles` |\n|---|---|\n| C1.1 | `\"Motivo: Miembro indica que es miembro actual pero no se proporcionó membresia_id válido. No se puede verificar el estado de la cuenta.\"` |\n| C1.2 | `\"Motivo: Plan actual del miembro no disponible o no reconocido ('{{plan_actual}}'). No se puede generar respuesta personalizada.\"` |\n| C1.3 | `\"Motivo: Nombre del contacto no disponible o genérico. No se puede personalizar la respuesta.\"` |\n| C1.4 | `\"Motivo: Teléfono del contacto '{{telefono_contacto}}' no cumple con el formato válido (8-15 dígitos). No es posible enviar respuesta por {{canal_origen}}.\"` |\n| C1.5 | `\"Motivo: Canal configurado como 'email' pero el contacto no tiene email registrado. No es posible enviar respuesta.\"` |\n| C1.6 | `\"Motivo: Email del contacto '{{email_remitente}}' no tiene formato válido. No es posible enviar respuesta por email.\"` |\n| C2.1 | `\"Motivo: No hay planes de membresía activos configurados en el sistema. No se puede responder consultas de precios o planes.\"` |\n| C2.2 | `\"Motivo: Plan '{{plan_consultado}}' no encontrado en la lista de planes disponibles. No se puede proporcionar información de precios.\"` |\n| C2.3 | `\"Motivo: Precio del plan '{{plan_consultado}}' no válido (${{precio_mensual}}). Verificar configuración de precios.\"` |\n| C2.4 | `\"Motivo: No hay servicios o clases configurados en el sistema. No se puede responder consultas sobre clases grupales.\"` |\n| C2.5 | `\"Motivo: Fecha de vencimiento de membresía no disponible o con formato inválido para el miembro {{nombre_contacto}} (membresía {{membresia_id}}). No se puede informar el estado.\"` |\n| C2.6 | `\"Motivo: Membresía de {{nombre_contacto}} sin fecha de vencimiento válida o vencida sin renovación procesada. Requiere revisión manual.\"` |\n| C3.1 | `\"Motivo: Error de envío por canal {{canal_origen}} tras 2 intentos fallidos al enviar respuesta a {{nombre_contacto}} (consulta {{consulta_id}}).\"` |\n| C3.2 | `\"Motivo: Canal de origen '{{canal_origen}}' no reconocido para la consulta {{consulta_id}}.\"` |\n| C3.3 | `\"Motivo: Destino de envío inválido o vacío para canal {{canal_origen}}.\"` |\n| C4.1 | `\"Motivo: Solicitud de cancelación de membresía de {{nombre_contacto}} (plan {{plan_actual}}, membresía {{membresia_id}}). Requiere procesamiento humano para verificar política de cancelación, penalización aplicable y ofrecer alternativas.\"` |\n| C4.2 | `\"Motivo: Solicitud de cambio de plan de menor precio ({{plan_actual}} → {{plan_nuevo_consultado}}) de {{nombre_contacto}}. Requiere verificación de política de downgrade y posible ofrecimiento de alternativas.\"` |\n| C4.3 | `\"Motivo: Solicitud de cambio de plan sin plan destino claro de {{nombre_contacto}}. No se puede procesar sin definir el plan deseado.\"` |\n| C5.1 | `\"Motivo: Consulta demasiado breve ({{longitud_mensaje}} caracteres) y no clasificable. No se puede determinar la intención del contacto.\"` |\n| C5.2 | `\"Motivo: Consulta clasificada como OTRO pero contiene keywords que sugieren tema no cubierto por el agente: '{{keywords_detectadas}}'. Requiere revisión humana.\"` |\n| C6.1 | `\"Motivo: Solicitud de clase de prueba sin horarios disponibles en los próximos 7 días. No se puede ofrecer alternativa automatizada.\"` |\n\n---\n\n## Ejemplo completo de activación\n\n**Condición activada:** C4.1 (solicitud de cancelación de membresía)\n\n**Contexto:** El contacto \"Laura Hernández\" solicita cancelar su membresía Elite. El plan actual es `elite`, la membresía `MEM-20260301-305` está activa con vencimiento el 2027-03-01.\n\n**Flujo del agente:**\n1. Recibe consulta: `\"Quiero cancelar mi membresía, ya no puedo ir al gym por mi nuevo trabajo\"`\n2. Clasifica como `CANCELACION`.\n3. Verifica que es miembro actual con datos válidos.\n4. Genera respuesta con política de cancelación.\n5. Activa escalamiento obligatorio (C4.1).\n6. Envía respuesta al contacto con mensaje de escalamiento.\n7. Genera registro de escalamiento.\n8. Notifica al equipo interno.\n\n**Respuesta al contacto:**\n\n> Hola Laura Hernández. 🏋️ Entendemos tu decisión.\n>\n> Para cancelar tu membresía de **Elite** en FitZone Premium:\n>\n> 📋 **Política de cancelación:**\n> Las cancelaciones deben realizarse con al menos 15 días de anticipación. Se aplica una penalización del 10% sobre el monto restante del contrato.\n>\n> 📞 Un asesor se pondrá en contacto contigo para procesar la cancelación y resolver cualquier duda.\n>\n> Si prefieres, llámanos al +52 55 4321 8765.\n> — FitZone Premium\n\n**Notificación al equipo interno:**\n\n> 📋 CONSULTA DE CANCELACIÓN — FitZone Premium\n> ==========================================\n>\n> **Contacto:** Laura Hernández\n> **Teléfono:** +52 55 1122 3344\n> **Canal:** whatsapp\n>\n> 📋 **Plan actual:** Elite\n> 📅 **Vigencia hasta:** 1 de marzo de 2027\n> ❓ **Motivo:** Cliente indica que ya no puede asistir por cambio de horario laboral.\n>\n> 💬 **Mensaje original:** \"Quiero cancelar mi membresía, ya no puedo ir al gym por mi nuevo trabajo\"\n>\n> 🔗 Registro: ENQ-20260823-163000-003\n> ⏱️ Plazo de respuesta: 4 horas\n\n**Registro de escalamiento:**\n\n```json\n{\n  \"registro_id\": \"ENQREG-20260823-163005-003\",\n  \"consulta_id\": \"ENQ-20260823-163000-003\",\n  \"evento\": \"escalamiento\",\n  \"fecha_hora_ejecucion\": \"2026-08-23T16:30:05\",\n  \"agente\": \"membership-enquiry-agent\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"nombre_contacto\": \"Laura Hernández\",\n  \"telefono_contacto\": \"+52 55 1122 3344\",\n  \"email_contacto\": null,\n  \"canal_origen\": \"whatsapp\",\n  \"es_miembro_actual\": true,\n  \"membresia_id\": \"MEM-20260301-305\",\n  \"plan_actual\": \"elite\",\n  \"categoria_consulta\": \"CANCELACION\",\n  \"mensaje_consulta\": \"Quiero cancelar mi membresía, ya no puedo ir al gym por mi nuevo trabajo\",\n  \"resultado\": \"escalamiento\",\n  \"detalles\": \"Motivo: Solicitud de cancelación de membresía de Laura Hernández (plan elite, membresía MEM-20260301-305). Requiere procesamiento humano para verificar política de cancelación, penalización aplicable y ofrecer alternativas.\",\n  \"responsable_escalamiento\": \"Carlos Mendoza\",\n  \"canal_escalamiento\": \"whatsapp_interno\",\n  \"plazo_respuesta_horas\": 4,\n  \"datos_origen\": {\n    \"consulta_id\": \"ENQ-20260823-163000-003\",\n    \"nombre_contacto\": \"Laura Hernández\",\n    \"telefono_contacto\": \"+52 55 1122 3344\",\n    \"categoria_consulta\": \"CANCELACION\",\n    \"es_miembro_actual\": true,\n    \"plan_actual\": \"elite\"\n  }\n}\n```\n"
              },
              {
                "id": "membership-enquiry-agent/variables-entrada.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "variables-entrada.md",
                "contenido": "# Variables de entrada — Membership Enquiry Agent\n\nEste archivo define la lista fija y cerrada de campos de datos que el agente necesita recibir para operar. No se admiten campos adicionales a los listados aquí.\n\n---\n\n## Campos del evento disparador\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 1 | `evento_id` | `string` | Obligatorio | Identificador único del evento. Formato: `ENQ-YYYYMMDD-HHMMSS-XXX`. | `\"ENQ-20260823-103000-001\"` |\n| 2 | `contacto_id` | `string` | Obligatorio | Identificador del contacto asociado. Formato: `CON-YYYYMMDD-XXX`. | `\"CON-20260823-201\"` |\n| 3 | `mensaje_consulta` | `string` | Obligatorio | Mensaje o texto libre de la consulta del contacto. Mínimo 3 caracteres, máximo 2000. | `\"Hola, ¿cuánto cuesta la membresía premium?\"` |\n| 4 | `canal_origen` | `string` (enum) | Obligatorio | Canal por el que se recibió la consulta. Valores: `whatsapp`, `sms`, `email`, `app`, `web`. | `\"whatsapp\"` |\n| 5 | `numero_remitente` | `string` | Obligatorio (requerido para whatsapp/sms) | Número telefónico del remitente. Entre 8 y 15 dígitos. | `\"+52 55 9876 5432\"` |\n| 6 | `email_remitente` | `string` | Opcional (requerido si `canal_origen` = `email`) | Correo electrónico del remitente. | `\"ana.garcia@correo.com\"` |\n| 7 | `fecha_evento` | `string` (ISO 8601: `YYYY-MM-DD`) | Obligatorio | Fecha en que se recibió la consulta. | `\"2026-08-23\"` |\n| 8 | `hora_evento` | `string` (formato 24h: `HH:MM`) | Opcional | Hora en que se recibió la consulta. | `\"10:30\"` |\n\n---\n\n## Datos del contacto\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 9 | `nombre_contacto` | `string` | Obligatorio | Nombre completo del contacto. | `\"Ana García López\"` |\n| 10 | `telefono_contacto` | `string` | Obligatorio | Número telefónico del contacto. Entre 8 y 15 dígitos. | `\"+52 55 9876 5432\"` |\n| 11 | `email_contacto` | `string` | Opcional | Correo electrónico del contacto. | `\"ana.garcia@correo.com\"` |\n| 12 | `es_miembro_actual` | `boolean` | Obligatorio | `true` si el contacto ya es miembro activo del gimnasio. `false` si es prospecto nuevo. | `true` |\n| 13 | `membresia_id` | `string` | Opcional (requerido si `es_miembro_actual` = `true`) | Identificador de la membresía activa. Formato: `MEM-YYYYMMDD-XXX`. | `\"MEM-20260115-101\"` |\n| 14 | `plan_actual` | `string` | Opcional (requerido si `es_miembro_actual` = `true`) | Plan de membresía actual del contacto. Valores: `basico`, `premium`, `elite`. | `\"premium\"` |\n| 15 | `fecha_inicio_membresia` | `string` (ISO 8601: `YYYY-MM-DD`) | Opcional (requerido si `es_miembro_actual` = `true`) | Fecha de inicio de la membresía actual. | `\"2026-01-15\"` |\n| 16 | `fecha_fin_membresia` | `string` (ISO 8601: `YYYY-MM-DD`) | Opcional (requerido si `es_miembro_actual` = `true`) | Fecha de vencimiento de la membresía actual. | `\"2026-12-15\"` |\n| 17 | `fecha_proximo_cobro` | `string` (ISO 8601: `YYYY-MM-DD`) | Opcional (requerido si `es_miembro_actual` = `true`) | Fecha del próximo cobro automático. | `\"2026-09-15\"` |\n| 18 | `monto_proximo_cobro` | `number` | Opcional (requerido si `es_miembro_actual` = `true`) | Monto del próximo cobro. | `699.00` |\n\n---\n\n## Datos del gimnasio (inyectados desde configuración)\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 19 | `gimnasio_id` | `string` | Obligatorio | Identificador único del gimnasio. | `\"GYM-001\"` |\n| 20 | `gimnasio_nombre` | `string` | Obligatorio | Nombre comercial del gimnasio. | `\"FitZone Premium\"` |\n| 21 | `gimnasio_telefono` | `string` | Obligatorio | Teléfono principal del gimnasio. | `\"+52 55 4321 8765\"` |\n| 22 | `gimnasio_email` | `string` | Obligatorio | Email general del gimnasio. | `\"info@fitzonepremium.com\"` |\n| 23 | `gimnasio_direccion` | `string` | Obligatorio | Dirección completa del gimnasio. | `\"Av. Constituyentes 1230, Col. Las Américas, CDMX\"` |\n| 24 | `zona_horaria` | `string` | Obligatorio | Zona horaria del gimnasio en formato IANA. | `\"America/Mexico_City\"` |\n| 25 | `horario_atencion_inicio` | `string` (formato 24h: `HH:MM`) | Obligatorio | Hora de inicio del horario de atención. | `\"06:00\"` |\n| 26 | `horario_atencion_fin` | `string` (formato 24h: `HH:MM`) | Obligatorio | Hora de fin del horario de atención. | `\"22:00\"` |\n| 27 | `idioma_respuesta` | `string` (enum) | Obligatorio | Idioma de las respuestas. Valores: `es`, `en`. | `\"es\"` |\n| 28 | `moneda` | `string` | Obligatorio | Código de moneda ISO 4217. | `\"MXN\"` |\n\n---\n\n## Planes y precios del gimnasio (inyectados desde configuración)\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 29 | `planes_disponibles` | `array` de objects | Obligatorio | Lista de planes de membresía disponibles. (ver estructura) | (ver abajo) |\n\n### Estructura de cada plan en `planes_disponibles`\n\n```json\n{\n  \"plan_id\": \"PLAN-BASICO\",\n  \"plan_nombre\": \"Básico\",\n  \"plan_codigo\": \"basico\",\n  \"precio_mensual\": 399.00,\n  \"precio_anual\": 3990.00,\n  \"beneficios\": [\n    \"Acceso a zona de gimnasio\",\n    \"Acceso a zona de cardio\",\n    \"Estacionamiento incluido\"\n  ],\n  \"beneficios_no_incluidos\": [\n    \"Clases grupales\",\n    \"Sauna y vapor\",\n    \"Entrenamiento personal\"\n  ],\n  \"activo\": true\n}\n```\n\n| Campo | Tipo | Obligatorio | Descripción |\n|---|---|---|---|\n| `plan_id` | `string` | Obligatorio | ID único del plan. Formato: `PLAN-XXXXX`. |\n| `plan_nombre` | `string` | Obligatorio | Nombre legible del plan. |\n| `plan_codigo` | `string` (enum) | Obligatorio | Código del plan: `basico`, `premium`, `elite`. |\n| `precio_mensual` | `number` | Obligatorio | Precio mensual del plan. |\n| `precio_anual` | `number` | Opcional | Precio de pago anual (si aplica descuento). `null` si no existe opción anual. |\n| `beneficios` | `array` de strings | Obligatorio | Lista de beneficios incluidos en el plan. |\n| `beneficios_no_incluidos` | `array` de strings | Opcional | Beneficios que este plan NO incluye. |\n| `activo` | `boolean` | Obligatorio | `true` si el plan está disponible para contratación. |\n\n---\n\n## Servicios y clases del gimnasio (inyectados desde configuración)\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 30 | `servicios_disponibles` | `array` de objects | Opcional | Lista de servicios y clases disponibles. (ver estructura) | (ver abajo) |\n\n### Estructura de cada servicio en `servicios_disponibles`\n\n```json\n{\n  \"servicio_id\": \"SRV-SPINNING\",\n  \"servicio_nombre\": \"Spinning\",\n  \"tipo\": \"clase_grupal\",\n  \"precio_suelto\": 80.00,\n  \"incluido_en_planes\": [\"premium\", \"elite\"],\n  \"horario\": [\n    {\"dia\": \"lunes\", \"hora_inicio\": \"07:00\", \"hora_fin\": \"08:00\"},\n    {\"dia\": \"miércoles\", \"hora_inicio\": \"07:00\", \"hora_fin\": \"08:00\"},\n    {\"dia\": \"viernes\", \"hora_inicio\": \"18:00\", \"hora_fin\": \"19:00\"}\n  ],\n  \"cupo_maximo\": 25,\n  \"activo\": true\n}\n```\n\n---\n\n## Políticas de cancelación (inyectadas desde configuración)\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 31 | `politica_cancelacion` | `object` | Obligatorio | Políticas de cancelación del gimnasio. (ver estructura) | (ver abajo) |\n\n### Estructura de `politica_cancelacion`\n\n```json\n{\n  \"dias_aviso_minimo\": 15,\n  \"penalizacion_porcentaje\": 10,\n  \"penalizacion_monto_fijo\": 0,\n  \"cancelacion_gratuita_dias_despues_inicio\": 7,\n  \"renovacion_automatica\": true,\n  \"descuento_renovacion_porcentaje\": 5,\n  \"texto_politica\": \"Las cancelaciones deben realizarse con al menos 15 días de anticipación. Se aplica una penalización del 10% sobre el monto restante del contrato.\"\n}\n```\n\n| Campo | Tipo | Descripción |\n|---|---|---|\n| `dias_aviso_minimo` | `integer` | Días mínimos de anticipación para cancelar sin penalización. |\n| `penalizacion_porcentaje` | `number` | Porcentaje de penalización sobre el monto restante. |\n| `penalizacion_monto_fijo` | `number` | Monto fijo de penalización (si aplica). `0` si se usa porcentaje. |\n| `cancelacion_gratuita_dias_despues_inicio` | `integer` | Días después del inicio en que se puede cancelar gratis. |\n| `renovacion_automatica` | `boolean` | `true` si la membresía se renueva automáticamente. |\n| `descuento_renovacion_porcentaje` | `number` | Descuento por renovación anticipada. |\n| `texto_politica` | `string` | Texto literal de la política para incluir en respuestas. |\n\n---\n\n## Datos del destinatario de notificación interna\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 32 | `responsable_nombre` | `string` | Obligatorio | Nombre del responsable que recibe notificaciones internas. | `\"Carlos Mendoza\"` |\n| 33 | `responsable_telefono` | `string` | Opcional | Teléfono del responsable. | `\"+52 55 2222 3333\"` |\n| 34 | `responsable_email` | `string` | Opcional | Email del responsable. | `\"carlos.mendoza@fitzonepremium.com\"` |\n| 35 | `canal_notificacion_equipo` | `string` (enum) | Obligatorio | Canal para notificar al equipo interno. Valores: `whatsapp_interno`, `email_interno`. | `\"whatsapp_interno\"` |\n\n---\n\n## Configuración de tiempos (inyectada desde configuración)\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 36 | `plazo_respuesta_horas` | `integer` | Obligatorio | Horas máximas de respuesta para consultas generales. Valor por defecto: `24`. | `24` |\n| 37 | `plazo_respuesta_cancelacion_horas` | `integer` | Obligatorio | Horas máximas de respuesta para consultas de cancelación. Valor por defecto: `4`. | `4` |\n| 38 | `plazo_clase_prueba_horas` | `integer` | Obligatorio | Horas máximas para confirmar reserva de clase de prueba. Valor por defecto: `2`. | `2` |\n| 39 | `hora_preferida_envio` | `string` (formato 24h: `HH:MM`) | Obligatorio | Hora preferida para enviar mensajes de seguimiento. Valor por defecto: `\"10:00\"`. | `\"10:00\"` |\n\n---\n\n## Campos calculados por el agente\n\nEstos campos **no se reciben de entrada**; el agente los genera internamente.\n\n| # | Nombre del campo | Tipo de dato | Generado en | Descripción |\n|---|---|---|---|---|\n| 40 | `consulta_id` | `string` | Paso 1 | ID único de la consulta. Formato: `ENQ-YYYYMMDD-HHMMSS-XXX`. |\n| 41 | `fecha_hora_recepcion` | `string` (ISO 8601) | Paso 1 | Marca de tiempo de recepción de la consulta. |\n| 42 | `categoria_consulta` | `string` (enum) | Paso 2 | Categoría clasificada de la consulta. |\n| 43 | `nombre_contacto_extraido` | `string` o `null` | Paso 2 | Nombre del contacto extraído del mensaje o perfil. |\n| 44 | `plan_consultado` | `string` o `null` | Paso 2 | Plan mencionado en la consulta. |\n| 45 | `servicio_consultado` | `string` o `null` | Paso 2 | Servicio o beneficio mencionado en la consulta. |\n| 46 | `informacion_no_disponible` | `boolean` | Paso 2 | `true` si la información solicitada no está en los datos de entrada. |\n| 47 | `accion_sugerida` | `string` | Paso 2 | Acción sugerida según la categoría. |\n| 48 | `texto_respuesta_personalizado` | `string` | Paso 3 | Texto principal de la respuesta. |\n| 49 | `accion_sugerida_texto` | `string` | Paso 3 | Texto de la acción sugerida al final del mensaje. |\n| 50 | `fecha_hora_envio` | `string` (ISO 8601) o `null` | Paso 4 | Marca de tiempo de envío de la respuesta. |\n| 51 | `estado_salida` | `string` (enum) | Paso 3–4 | Estado: `generada`, `enviada`, `error_envio`. |\n| 52 | `fecha_hora_cierre` | `string` (ISO 8601) | Paso 5 | Marca de tiempo de cierre del caso. |\n| 53 | `resultado` | `string` (enum) | Paso 5 | Resultado: `informacion_entregada`, `clase_prueba_reservada`, `escalamiento`, `consulta_incompleta`, `envio_fallido`. |\n| 54 | `duracion_proceso_segundos` | `integer` | Paso 5 | Duración total del proceso en segundos. |\n\n---\n\n## Validaciones de entrada\n\n| Campo | Regla | Acción si falla |\n|---|---|---|\n| `evento_id` | No vacío, formato válido | Terminar flujo sin ejecutar |\n| `contacto_id` | Debe seguir formato `CON-XXXXXXXX-XXX` | Terminar flujo sin ejecutar |\n| `mensaje_consulta` | No vacío, mínimo 3 caracteres, máximo 2000 | Terminar flujo sin ejecutar |\n| `canal_origen` | Debe ser uno de los 5 valores válidos | Usar `\"whatsapp\"` como valor por defecto |\n| `numero_remitente` | Entre 8 y 15 dígitos si `canal_origen` es `whatsapp` o `sms` | Escalar a humano (C3.1) |\n| `email_remitente` | Si `canal_origen` es `email`, debe ser formato válido | Escalar a humano (C3.1) |\n| `nombre_contacto` | No vacío, mínimo 2 caracteres | Usar `\"Contacto\"` como valor por defecto |\n| `telefono_contacto` | Entre 8 y 15 dígitos | Terminar flujo sin ejecutar |\n| `es_miembro_actual` | Debe ser `true` o `false` | Usar `false` como valor por defecto |\n| `membresia_id` | Si `es_miembro_actual` es `true`, debe seguir formato `MEM-XXXXXXXX-XXX` | Escalar a humano (C1.1) |\n| `plan_actual` | Si `es_miembro_actual` es `true`, debe ser `basico`, `premium` o `elite` | Escalar a humano (C1.2) |\n| `gimnasio_id` | No vacío | Terminar flujo sin ejecutar |\n| `gimnasio_nombre` | No vacío | Terminar flujo sin ejecutar |\n| `gimnasio_telefono` | No vacío, formato válido | Terminar flujo sin ejecutar |\n| `moneda` | Código ISO 4217 válido | Usar `\"MXN\"` como valor por defecto |\n| `planes_disponibles` | Array con al menos 1 plan activo | Escalar a humano (C2.1) |\n| `responsable_nombre` | No vacío | Usar `\"Asesor de Membresías\"` como valor por defecto |\n| `canal_notificacion_equipo` | Debe ser uno de los 2 valores válidos | Usar `\"whatsapp_interno\"` como valor por defecto |\n"
              }
            ]
          }
        ]
      },
      {
        "id": "trial-booking",
        "carpeta": "trial-booking-agent",
        "nombre": "Trial Booking Agent",
        "icono": "📅",
        "descripcion": "Gestionar reservas de clases de prueba gratuitas para nuevos clientes",
        "submodulos": [],
        "categoria": "general",
        "estructura": [
          {
            "id": "trial-booking-agent",
            "etiqueta": "",
            "es_plantilla": true,
            "nombre": "trial-booking-agent",
            "subcarpetas": [],
            "notas": [
              {
                "id": "trial-booking-agent/flujo.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "flujo.md",
                "contenido": "# Flujo de ejecución — Trial Booking Agent\n\nEste archivo documenta el flujo fijo de ejecución del agente. Cada solicitud de reserva sigue estos 6 pasos **en orden**, sin pasos condicionales ni bifurcaciones.\n\n---\n\n## Paso 1 — Recepción de la solicitud de cita/reserva con fecha y servicio deseado\n\nEl agente recibe la solicitud de una clase de prueba, visita guiada o evaluación física de un contacto (prospecto nuevo o miembro actual) a través de cualquier canal configurado.\n\n**Acción del agente:**\n1. Recibir el evento disparador `solicitud_reserva`.\n2. Extraer los campos de entrada definidos en `variables-entrada.md`.\n3. Registrar la marca de tiempo de recepción en `fecha_hora_recepcion`.\n4. Generar un `solicitud_id` único para el caso.\n5. Verificar que la solicitud contenga al menos un tipo de cita deseado (`tipo_cita_solicitada`).\n6. Extraer la `fecha_preferida` y `hora_preferida` si el usuario las indicó en su mensaje.\n\n**Ejemplo de entrada:**\n\n```json\n{\n  \"evento\": \"solicitud_reserva\",\n  \"canal_origen\": \"whatsapp\",\n  \"numero_remitente\": \"+52 55 9876 5432\",\n  \"mensaje\": \"Hola, quiero agendar una clase de prueba de spinning, ¿tienen disponibilidad el lunes?\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"gimnasio_telefono\": \"+52 55 4321 8765\"\n}\n```\n\n---\n\n## Paso 2 — Consulta de disponibilidad en el calendario conectado\n\nEl agente consulta el sistema de calendario del gimnasio para verificar la disponibilidad de horarios según el tipo de cita y la fecha/hora preferida.\n\n**Acción del agente:**\n1. Clasificar el `tipo_cita_solicitada` según la tabla de tipos de cita de `prompt-sistema.md`.\n2. Determinar la `fecha_preferida` solicitada (si el usuario la indicó). Si no la indicó, usar la fecha actual o el siguiente día hábil.\n3. Determinar la `hora_preferida` solicitada (si el usuario la indicó). Si no la indicó, usar cualquier horario disponible dentro del horario de atención.\n4. Consultar el calendario conectado para obtener los horarios disponibles en los próximos **7 días naturales** a partir de la `fecha_preferida`.\n5. Filtrar los resultados excluyendo:\n   - Horarios fuera del horario de atención (`horario_atencion_inicio` – `horario_atencion_fin`).\n   - Horarios que se superpongan con clases o eventos ya agendados.\n   - Horarios en días no hábiles (según la configuración del calendario).\n6. Clasificar cada horario disponible en `disponibilidad_horarios`:\n   - `disponible`: el horario tiene cupo completo y no hay conflictos.\n   - `limitado`: el horario tiene menos del 30% de cupo restante.\n   - `lleno`: el horario no tiene cupo (se excluye de las opciones).\n7. Seleccionar los **3 mejores horarios** para ofrecer al usuario, priorizando:\n   - Horarios más cercanos a la `fecha_preferida` y `hora_preferida`.\n   - Horarios con mayor cupo disponible.\n   - Horarios dentro del mismo día de la semana si fue solicitado.\n\n**Ejemplo de consulta al calendario:**\n\n```json\n{\n  \"solicitud_id\": \"TRB-20260823-103000-001\",\n  \"tipo_cita_solicitada\": \"CLASE_PRUEBA_GRUPAL\",\n  \"clase_especifica\": \"spinning\",\n  \"fecha_preferida\": \"2026-08-25\",\n  \"hora_preferida\": null,\n  \"resultado_consulta\": {\n    \"horarios_encontrados\": 5,\n    \"horarios_disponibles\": [\n      {\"fecha\": \"2026-08-25\", \"hora_inicio\": \"07:00\", \"hora_fin\": \"08:00\", \"clase\": \"Spinning\", \"instructor\": \"Coach Martínez\", \"cupo_disponible\": 15, \"cupo_total\": 25, \"disponibilidad\": \"disponible\"},\n      {\"fecha\": \"2026-08-25\", \"hora_inicio\": \"18:00\", \"hora_fin\": \"19:00\", \"clase\": \"Spinning\", \"instructor\": \"Coach López\", \"cupo_disponible\": 8, \"cupo_total\": 25, \"disponibilidad\": \"limitado\"},\n      {\"fecha\": \"2026-08-27\", \"hora_inicio\": \"07:00\", \"hora_fin\": \"08:00\", \"clase\": \"Spinning\", \"instructor\": \"Coach Martínez\", \"cupo_disponible\": 20, \"cupo_total\": 25, \"disponibilidad\": \"disponible\"},\n      {\"fecha\": \"2026-08-29\", \"hora_inicio\": \"07:00\", \"hora_fin\": \"08:00\", \"clase\": \"Spinning\", \"instructor\": \"Coach Martínez\", \"cupo_disponible\": 22, \"cupo_total\": 25, \"disponibilidad\": \"disponible\"},\n      {\"fecha\": \"2026-08-29\", \"hora_inicio\": \"18:00\", \"hora_fin\": \"19:00\", \"clase\": \"Spinning\", \"instructor\": \"Coach López\", \"cupo_disponible\": 0, \"cupo_total\": 25, \"disponibilidad\": \"lleno\"}\n    ],\n    \"horarios_seleccionados_para_ofrecer\": [\n      {\"fecha\": \"2026-08-25\", \"hora_inicio\": \"07:00\", \"hora_fin\": \"08:00\", \"clase\": \"Spinning\", \"instructor\": \"Coach Martínez\", \"cupo_disponible\": 15},\n      {\"fecha\": \"2026-08-25\", \"hora_inicio\": \"18:00\", \"hora_fin\": \"19:00\", \"clase\": \"Spinning\", \"instructor\": \"Coach López\", \"cupo_disponible\": 8},\n      {\"fecha\": \"2026-08-27\", \"hora_inicio\": \"07:00\", \"hora_fin\": \"08:00\", \"clase\": \"Spinning\", \"instructor\": \"Coach Martínez\", \"cupo_disponible\": 20}\n    ]\n  }\n}\n```\n\n---\n\n## Paso 3 — Ofrecimiento de hasta 3 horarios disponibles concretos\n\nEl agente genera y envía al usuario un mensaje con las opciones de horario disponibles para su clase de prueba.\n\n**Acción del agente:**\n1. Seleccionar los 3 horarios del `horarios_seleccionados_para_ofrecer` del paso 2.\n2. Formatear cada opción con fecha completa (día de semana + día + mes + año), hora de inicio y fin, nombre de la clase y nombre del instructor.\n3. Generar el mensaje de ofrecimiento usando la plantilla de `formato-salida.md` → sección 1.\n4. Enviar el mensaje por el `canal_origen`.\n5. Si el envío falla, reintentar una sola vez después de 30 segundos.\n6. Si el segundo intento falla, registrar el error y activar escalamiento (ver `reglas-escalamiento.md` → C3.1).\n7. Registrar la marca de tiempo de envío en `fecha_hora_envio_oferta`.\n8. Actualizar `estado_reserva` a `oferta_enviada`.\n9. Iniciar el contador de plazo de respuesta: **24 horas** desde el envío de la oferta.\n10. Calcular el `momento_expiracion_oferta`: `fecha_hora_envio_oferta` + 24 horas.\n\n**Plantilla de ofrecimiento de horarios (whatsapp/sms):**\n\n```\n¡Hola {{nombre_contacto}}! 🏋️ Consulté la disponibilidad para tu {{tipo_cita_nombre}} y estas son las opciones:\n\n📅 **Opción 1:** {{dia_semana_op1}} {{fecha_op1_formateada}} a las {{hora_inicio_op1}} — {{clase_op1}} con {{instructor_op1}} ({{cupo_op1}} lugares)\n📅 **Opción 2:** {{dia_semana_op2}} {{fecha_op2_formateada}} a las {{hora_inicio_op2}} — {{clase_op2}} con {{instructor_op2}} ({{cupo_op2}} lugares)\n📅 **Opción 3:** {{dia_semana_op3}} {{fecha_op3_formateada}} a las {{hora_inicio_op3}} — {{clase_op3}} con {{instructor_op3}} ({{cupo_op3}} lugares)\n\n¿Cuál te conviene más? Responde con el número (1, 2 o 3).\n— {{gimnasio_nombre}}\n```\n\n**Ejemplo renderizado:**\n\n> ¡Hola Ana García! 🏋️ Consulté la disponibilidad para tu Clase de prueba grupal de Spinning y estas son las opciones:\n>\n> 📅 **Opción 1:** Lunes 25 de agosto de 2026 a las 07:00 — Spinning con Coach Martínez (15 lugares)\n> 📅 **Opción 2:** Lunes 25 de agosto de 2026 a las 18:00 — Spinning con Coach López (8 lugares)\n> 📅 **Opción 3:** Miércoles 27 de agosto de 2026 a las 07:00 — Spinning con Coach Martínez (20 lugares)\n>\n> ¿Cuál te conviene más? Responde con el número (1, 2 o 3).\n> — FitZone Premium\n\n---\n\n## Paso 4 — Confirmación de la selección del usuario y creación del evento en el calendario\n\nEl agente recibe la selección del usuario, verifica la disponibilidad del horario seleccionado y crea el evento en el calendario del gimnasio.\n\n**Acción del agente:**\n1. Recibir la respuesta del usuario con su selección (número de opción: `1`, `2` o `3`).\n2. Validar que la selección sea uno de los 3 números válidos:\n   - Si es válido → proceder con la reserva.\n   - Si no es válido → enviar mensaje pidiendo que responda con 1, 2 o 3.\n3. Verificar que el horario seleccionado **aún esté disponible** en el calendario (puede haber cambiado desde el paso 2):\n   - Si está disponible → proceder a crear el evento.\n   - Si ya no está disponible → informar al usuario y ofrecer las opciones restantes.\n4. Crear el evento en el calendario conectado con los siguientes datos:\n   - `evento_calendario_id`: ID del evento creado en el sistema.\n   - `fecha_evento`: fecha de la reserva.\n   - `hora_inicio`: hora de inicio.\n   - `hora_fin`: hora de fin calculada = `hora_inicio` + `duracion_tipo_cita` minutos.\n   - `tipo_cita`: tipo de cita seleccionada.\n   - `clase_nombre`: nombre de la clase (si aplica).\n   - `instructor`: nombre del instructor (si aplica).\n   - `contacto_id`: ID del contacto.\n   - `estado`: `confirmada`.\n5. Generar el `reserva_id` único para esta reserva. Formato: `TRB-YYYYMMDD-HHMMSS-XXX`.\n6. Registrar la marca de tiempo de confirmación en `fecha_hora_confirmacion`.\n7. Actualizar `estado_reserva` de `oferta_enviada` a `confirmada`.\n\n**Tabla de duración por tipo de cita:**\n\n| Tipo de cita | Duración (min) | Ejemplo hora_fin |\n|---|---|---|\n| `CLASE_PRUEBA_GRUPAL` | 60 | 07:00 → 08:00 |\n| `VISITA_GUIADA` | 30 | 10:00 → 10:30 |\n| `EVALUACION_FISICA` | 45 | 09:00 → 09:45 |\n| `SESION_PERSONAL_PRUEBA` | 60 | 16:00 → 17:00 |\n\n**Ejemplo de creación de evento:**\n\n```json\n{\n  \"evento_calendario_id\": \"EVT-20260823-103045-501\",\n  \"reserva_id\": \"TRB-20260823-103000-001\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"contacto_id\": \"CON-20260823-201\",\n  \"nombre_contacto\": \"Ana García López\",\n  \"tipo_cita\": \"CLASE_PRUEBA_GRUPAL\",\n  \"clase_nombre\": \"Spinning\",\n  \"instructor\": \"Coach Martínez\",\n  \"fecha_evento\": \"2026-08-25\",\n  \"hora_inicio\": \"07:00\",\n  \"hora_fin\": \"08:00\",\n  \"duracion_minutos\": 60,\n  \"precio\": 0.00,\n  \"moneda\": \"MXN\",\n  \"estado\": \"confirmada\",\n  \"fecha_hora_creacion\": \"2026-08-23T10:30:45\"\n}\n```\n\n---\n\n## Paso 5 — Envío de confirmación por escrito al usuario con fecha, hora y detalles\n\nEl agente envía al usuario un mensaje de confirmación completo con todos los datos de la reserva.\n\n**Acción del agente:**\n1. Generar el mensaje de confirmación usando la plantilla de `formato-salida.md` → sección 2.\n2. Incluir en el mensaje:\n   - Código de reserva (`reserva_id`).\n   - Fecha completa formateada (día de semana + día + mes + año).\n   - Hora de inicio y fin.\n   - Nombre de la clase o tipo de cita.\n   - Nombre del instructor (si aplica).\n   - Dirección del gimnasio.\n   - Precio (Gratis para clase de prueba).\n   - Tips para la primera visita.\n3. Enviar el mensaje por el `canal_origen`.\n4. Si el envío falla, reintentar una sola vez después de 30 segundos.\n5. Si el segundo intento falla, registrar el error y activar escalamiento (ver `reglas-escalamiento.md` → C3.2).\n6. Registrar la marca de tiempo de envío en `fecha_hora_envio_confirmacion`.\n7. Actualizar `estado_envio_confirmacion` a `enviada`.\n\n**Plantilla de confirmación (whatsapp/sms):**\n\n```\n¡Reserva confirmada! 🎉\n\n📅 **Fecha:** {{dia_semana_reserva}} {{fecha_reserva_formateada}}\n🕐 **Hora:** {{hora_inicio}}–{{hora_fin}}\n🏋️ **Clase:** {{clase_nombre}}\n👨‍🏫 **Instructor:** {{instructor_nombre}}\n📍 **Lugar:** {{gimnasio_nombre}} — {{gimnasio_direccion}}\n💳 **Precio:** {{texto_precio}}\n\nTu código de reserva es **{{reserva_id}}**. Guárdalo para futuras consultas.\n\n💡 **Tips para tu primera visita:**\n• Llega 10 minutos antes.\n• Trae ropa deportiva y tenis cómodos.\n• Trae una botella de agua.\n\n{{texto_recordatorio}}\n\n¡Te esperamos! 💪\n— {{gimnasio_nombre}}\n```\n\n**Plantilla de confirmación (email):**\n\n**Asunto:** Reserva confirmada — {{clase_nombre}} {{fecha_reserva_formateada}} — {{gimnasio_nombre}}\n\n```\nHola {{nombre_contacto}},\n\nTu reserva ha sido confirmada en {{gimnasio_nombre}}.\n\n📅 **Fecha:** {{dia_semana_reserva}} {{fecha_reserva_formateada}}\n🕐 **Hora:** {{hora_inicio}}–{{hora_fin}}\n🏋️ **Clase:** {{clase_nombre}}\n👨‍🏫 **Instructor:** {{instructor_nombre}}\n📍 **Dirección:** {{gimnasio_direccion}}\n💳 **Precio:** {{texto_precio}}\n\n**Código de reserva:** {{reserva_id}}\n\n💡 **Tips para tu primera visita:**\n• Llega 10 minutos antes.\n• Trae ropa deportiva y tenis cómodos.\n• Trae una botella de agua.\n\nTe enviaremos un recordatorio 24 horas antes de tu clase.\n\n¡Te esperamos! 💪\n— {{gimnasio_nombre}}\n```\n\n**Ejemplo renderizado (whatsapp):**\n\n> ¡Reserva confirmada! 🎉\n>\n> 📅 **Fecha:** Lunes 25 de agosto de 2026\n> 🕐 **Hora:** 07:00–08:00\n> 🏋️ **Clase:** Spinning\n> 👨‍🏫 **Instructor:** Coach Martínez\n> 📍 **Lugar:** FitZone Premium — Av. Constituyentes 1230, Col. Las Américas, CDMX\n> 💳 **Precio:** GRATIS (clase de prueba)\n>\n> Tu código de reserva es **TRB-20260823-103000-001**. Guárdalo para futuras consultas.\n>\n> 💡 **Tips para tu primera visita:**\n> • Llega 10 minutos antes.\n> • Trae ropa deportiva y tenis cómodos.\n> • Trae una botella de agua.\n>\n> 📲 Te enviaremos un recordatorio 24 horas antes de tu clase.\n>\n> ¡Te esperamos! 💪\n> — FitZone Premium\n\n---\n\n## Paso 6 — Registro del nuevo contacto o actualización del existente en la base de datos\n\nEl agente registra o actualiza la información del contacto en la base de datos del gimnasio.\n\n**Acción del agente:**\n1. Verificar si el `contacto_id` ya existe en la base de datos:\n   - Si existe → actualizar los campos necesarios (última interacción, historial de reservas).\n   - Si no existe → crear un nuevo registro de contacto.\n2. Para un **contacto nuevo**, crear el registro con:\n   - `contacto_id`: ID proporcionado o generado.\n   - `nombre`: nombre del contacto.\n   - `telefono`: número telefónico.\n   - `email`: correo electrónico (si está disponible).\n   - `fuente_contacto`: `\"clase_prueba\"`.\n   - `fecha_primer_contacto`: marca de tiempo actual.\n   - `ultima_interaccion`: marca de tiempo actual.\n   - `reservas`: array con la reserva recién creada.\n   - `estado_contacto`: `\"prospecto\"`.\n3. Para un **contacto existente**, actualizar:\n   - `ultima_interaccion`: marca de tiempo actual.\n   - `reservas`: agregar la nueva reserva al array existente.\n4. Generar el registro completo de la reserva con el formato de `formato-salida.md` → sección 4 (registro JSON).\n5. Registrar la marca de tiempo de cierre en `fecha_hora_cierre`.\n6. Calcular `duracion_proceso_segundos` = `fecha_hora_cierre` - `fecha_hora_recepcion`.\n7. Actualizar `estado_reserva` a `completada`.\n8. Guardar el registro en el sistema de historial de contactos.\n\n**Tabla de resultados posibles:**\n\n| Resultado | Descripción | Próxima acción del sistema |\n|---|---|---|\n| `reserva_confirmada` | Se creó la reserva exitosamente en el calendario | Enviar recordatorio 24h antes. Seguimiento post-clase en 24h. |\n| `contacto_nuevo_creado` | Se creó un nuevo registro de contacto | Agregar a lista de nurturing de prospectos. |\n| `contacto_actualizado` | Se actualizó el registro de contacto existente | Actualizar perfil en CRM. |\n| `escalamiento` | Se escaló a humano por condición de `reglas-escalamiento.md` | Esperar resolución del humano. Plazo según categoría. |\n| `oferta_expirada` | El usuario no respondió en 24 horas | Cerrar caso. No se envía más mensajes. |\n| `envio_fallido` | No se pudo entregar la confirmación al contacto | Escalar a humano (C3.2). Registro de error guardado. |\n"
              },
              {
                "id": "trial-booking-agent/formato-salida.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "formato-salida.md",
                "contenido": "# Formato de salida — Trial Booking Agent\n\nEste archivo define la estructura exacta de los mensajes y registros que el agente debe producir. No se permite desviarse de estas plantillas.\n\n---\n\n## 1. Ofrecimiento de horarios disponibles — Mensaje al contacto\n\nSe genera en el paso 3 del flujo. Se envía por el canal configurado (`canal_origen`).\n\n### Plantilla — Ofrecimiento de horarios (whatsapp/sms)\n\n```\n¡Hola {{nombre_contacto}}! 🏋️ Consulté la disponibilidad para tu {{tipo_cita_nombre}} y estas son las opciones:\n\n📅 **Opción 1:** {{dia_semana_op1}} {{fecha_op1_formateada}} a las {{hora_inicio_op1}} — {{clase_op1}} con {{instructor_op1}} ({{cupo_op1}} lugares disponibles)\n📅 **Opción 2:** {{dia_semana_op2}} {{fecha_op2_formateada}} a las {{hora_inicio_op2}} — {{clase_op2}} con {{instructor_op2}} ({{cupo_op2}} lugares disponibles)\n📅 **Opción 3:** {{dia_semana_op3}} {{fecha_op3_formateada}} a las {{hora_inicio_op3}} — {{clase_op3}} con {{instructor_op3}} ({{cupo_op3}} lugares disponibles)\n\n¿Cuál te conviene más? Responde con el número (1, 2 o 3).\n— {{gimnasio_nombre}}\n```\n\n### Plantilla — Ofrecimiento de horarios (email)\n\n**Asunto:** Horarios disponibles para tu {{tipo_cita_nombre}} — {{gimnasio_nombre}}\n\n```\nHola {{nombre_contacto}},\n\nConsultamos la disponibilidad para tu {{tipo_cita_nombre}} en {{gimnasio_nombre}}. Estas son las opciones:\n\n1. {{dia_semana_op1}} {{fecha_op1_formateada}} a las {{hora_inicio_op1}} — {{clase_op1}} con {{instructor_op1}} ({{cupo_op1}} lugares disponibles)\n2. {{dia_semana_op2}} {{fecha_op2_formateada}} a las {{hora_inicio_op2}} — {{clase_op2}} con {{instructor_op2}} ({{cupo_op2}} lugares disponibles)\n3. {{dia_semana_op3}} {{fecha_op3_formateada}} a las {{hora_inicio_op3}} — {{clase_op3}} con {{instructor_op3}} ({{cupo_op3}} lugares disponibles)\n\nResponde a este correo con el número de tu opción preferida (1, 2 o 3).\n\n¡Te esperamos! 💪\n— {{gimnasio_nombre}}\n```\n\n### Ejemplo renderizado (whatsapp)\n\n> ¡Hola Ana García! 🏋️ Consulté la disponibilidad para tu Clase de prueba grupal de Spinning y estas son las opciones:\n>\n> 📅 **Opción 1:** Lunes 25 de agosto de 2026 a las 07:00 — Spinning con Coach Martínez (15 lugares disponibles)\n> 📅 **Opción 2:** Lunes 25 de agosto de 2026 a las 18:00 — Spinning con Coach López (8 lugares disponibles)\n> 📅 **Opción 3:** Miércoles 27 de agosto de 2026 a las 07:00 — Spinning con Coach Martínez (20 lugares disponibles)\n>\n> ¿Cuál te conviene más? Responde con el número (1, 2 o 3).\n> — FitZone Premium\n\n### Ejemplo renderizado (email)\n\n> **Asunto:** Horarios disponibles para tu Clase de prueba grupal de Spinning — FitZone Premium\n>\n> Hola Ana García,\n>\n> Consultamos la disponibilidad para tu Clase de prueba grupal de Spinning en FitZone Premium. Estas son las opciones:\n>\n> 1. Lunes 25 de agosto de 2026 a las 07:00 — Spinning con Coach Martínez (15 lugares disponibles)\n> 2. Lunes 25 de agosto de 2026 a las 18:00 — Spinning con Coach López (8 lugares disponibles)\n> 3. Miércoles 27 de agosto de 2026 a las 07:00 — Spinning con Coach Martínez (20 lugares disponibles)\n>\n> Responde a este correo con el número de tu opción preferida (1, 2 o 3).\n>\n> ¡Te esperamos! 💪\n> — FitZone Premium\n\n---\n\n## 2. Confirmación de reserva — Mensaje al contacto\n\nSe genera en el paso 5 del flujo. Se envía después de que el usuario selecciona un horario y se crea el evento en el calendario.\n\n### Plantilla — Confirmación de reserva (whatsapp/sms)\n\n```\n¡Reserva confirmada! 🎉\n\n📅 **Fecha:** {{dia_semana_reserva}} {{fecha_reserva_formateada}}\n🕐 **Hora:** {{hora_inicio}}–{{hora_fin}}\n🏋️ **Clase:** {{clase_nombre}}\n👨‍🏫 **Instructor:** {{instructor_nombre}}\n📍 **Lugar:** {{gimnasio_nombre}} — {{gimnasio_direccion}}\n💳 **Precio:** {{texto_precio}}\n\nTu código de reserva es **{{reserva_id}}**. Guárdalo para futuras consultas.\n\n💡 **Tips para tu primera visita:**\n• Llega 10 minutos antes.\n• Trae ropa deportiva y tenis cómodos.\n• Trae una botella de agua.\n\n{{texto_recordatorio}}\n\n¡Te esperamos! 💪\n— {{gimnasio_nombre}}\n```\n\n### Plantilla — Confirmación de reserva (email)\n\n**Asunto:** Reserva confirmada — {{clase_nombre}} {{fecha_reserva_formateada}} — {{gimnasio_nombre}}\n\n```\nHola {{nombre_contacto}},\n\nTu reserva ha sido confirmada en {{gimnasio_nombre}}.\n\n📅 **Fecha:** {{dia_semana_reserva}} {{fecha_reserva_formateada}}\n🕐 **Hora:** {{hora_inicio}}–{{hora_fin}}\n🏋️ **Clase:** {{clase_nombre}}\n👨‍🏫 **Instructor:** {{instructor_nombre}}\n📍 **Dirección:** {{gimnasio_direccion}}\n💳 **Precio:** {{texto_precio}}\n\n**Código de reserva:** {{reserva_id}}\n\n💡 **Tips para tu primera visita:**\n• Llega 10 minutos antes.\n• Trae ropa deportiva y tenis cómodos.\n• Trae una botella de agua.\n\nTe enviaremos un recordatorio 24 horas antes de tu clase.\n\n¡Te esperamos! 💪\n— {{gimnasio_nombre}}\n```\n\n### Plantilla — Confirmación de reserva (push)\n\n**Título:** ✅ Reserva confirmada en {{gimnasio_nombre}}\n**Cuerpo:** {{clase_nombre}} el {{fecha_reserva_formateada}} a las {{hora_inicio}}. Toca para ver detalles.\n\n### Ejemplo renderizado (whatsapp)\n\n> ¡Reserva confirmada! 🎉\n>\n> 📅 **Fecha:** Lunes 25 de agosto de 2026\n> 🕐 **Hora:** 07:00–08:00\n> 🏋️ **Clase:** Spinning\n> 👨‍🏫 **Instructor:** Coach Martínez\n> 📍 **Lugar:** FitZone Premium — Av. Constituyentes 1230, Col. Las Américas, CDMX\n> 💳 **Precio:** GRATIS (clase de prueba)\n>\n> Tu código de reserva es **TRB-20260823-103000-001**. Guárdalo para futuras consultas.\n>\n> 💡 **Tips para tu primera visita:**\n> • Llega 10 minutos antes.\n> • Trae ropa deportiva y tenis cómodos.\n> • Trae una botella de agua.\n>\n> 📲 Te enviaremos un recordatorio 24 horas antes de tu clase.\n>\n> ¡Te esperamos! 💪\n> — FitZone Premium\n\n---\n\n## 3. Notificación de reserva al equipo interno\n\n### Plantilla (whatsapp_interno)\n\n```\n📋 NUEVA RESERVA DE PRUEBA — {{gimnasio_nombre}}\n==========================================\n\n**Contacto:** {{nombre_contacto}}\n**Teléfono:** {{telefono_contacto}}\n**Canal:** {{canal_origen}}\n\n🏋️ **Tipo de cita:** {{tipo_cita_nombre}}\n📅 **Fecha:** {{dia_semana_reserva}} {{fecha_reserva_formateada}}\n🕐 **Hora:** {{hora_inicio}}–{{hora_fin}}\n👨‍🏫 **Clase:** {{clase_nombre}}\n👨‍🏫 **Instructor:** {{instructor_nombre}}\n💳 **Precio:** {{texto_precio}}\n\n🔗 Registro: {{reserva_id}}\n🔗 Evento calendario: {{evento_calendario_id}}\n```\n\n### Plantilla (email_interno)\n\n**Asunto:** Nueva reserva de prueba — {{nombre_contacto}} — {{gimnasio_nombre}}\n\n```\nNueva reserva de clase de prueba registrada en {{gimnasio_nombre}}.\n\nContacto: {{nombre_contacto}}\nTeléfono: {{telefono_contacto}}\nCanal: {{canal_origen}}\n\nTipo de cita: {{tipo_cita_nombre}}\nFecha: {{dia_semana_reserva}} {{fecha_reserva_formateada}}\nHora: {{hora_inicio}}–{{hora_fin}}\nClase: {{clase_nombre}}\nInstructor: {{instructor_nombre}}\nPrecio: {{texto_precio}}\n\nRegistro: {{reserva_id}}\nEvento calendario: {{evento_calendario_id}}\n```\n\n### Ejemplo renderizado (whatsapp_interno)\n\n> 📋 NUEVA RESERVA DE PRUEBA — FitZone Premium\n> ==========================================\n>\n> **Contacto:** Ana García López\n> **Teléfono:** +52 55 9876 5432\n> **Canal:** whatsapp\n>\n> 🏋️ **Tipo de cita:** Clase de prueba grupal\n> 📅 **Fecha:** Lunes 25 de agosto de 2026\n> 🕐 **Hora:** 07:00–08:00\n> 👨‍🏫 **Clase:** Spinning\n> 👨‍🏫 **Instructor:** Coach Martínez\n> 💳 **Precio:** GRATIS (clase de prueba)\n>\n> 🔗 Registro: TRB-20260823-103000-001\n> 🔗 Evento calendario: EVT-20260823-103045-501\n\n---\n\n## 4. Registro de reserva completa (JSON)\n\nSe genera al finalizar el paso 6 del flujo para cada reserva procesada.\n\n```json\n{\n  \"registro_id\": \"TRBREG-YYYYMMDD-HHMMSS-XXX\",\n  \"solicitud_id\": \"TRB-YYYYMMDD-HHMMSS-XXX\",\n  \"reserva_id\": \"TRB-YYYYMMDD-HHMMSS-XXX\",\n  \"evento_calendario_id\": \"EVT-YYYYMMDD-HHMMSS-XXX | null\",\n  \"contacto_id\": \"CON-YYYYMMDD-XXX\",\n  \"gimnasio_id\": \"string\",\n  \"gimnasio_nombre\": \"string\",\n  \"nombre_contacto\": \"string\",\n  \"telefono_contacto\": \"string\",\n  \"email_contacto\": \"string | null\",\n  \"canal_origen\": \"whatsapp | sms | email | app | web\",\n  \"es_miembro_actual\": true | false,\n  \"membresia_id\": \"string | null\",\n  \"plan_actual\": \"basico | premium | elite | null\",\n  \"tipo_cita_solicitada\": \"CLASE_PRUEBA_GRUPAL | VISITA_GUIADA | EVALUACION_FISICA | SESION_PERSONAL_PRUEBA\",\n  \"clase_especifica\": \"string | null\",\n  \"clase_nombre\": \"string | null\",\n  \"instructor_nombre\": \"string | null\",\n  \"fecha_preferida\": \"YYYY-MM-DD | null\",\n  \"hora_preferida\": \"HH:MM | null\",\n  \"notas_especiales\": \"string | null\",\n  \"mensaje_solicitud\": \"string\",\n  \"fecha_hora_recepcion\": \"YYYY-MM-DDTHH:MM:SS\",\n  \"horarios_ofrecidos\": [\n    {\"fecha\": \"YYYY-MM-DD\", \"hora_inicio\": \"HH:MM\", \"hora_fin\": \"HH:MM\", \"clase\": \"string\", \"instructor\": \"string\", \"cupo_disponible\": 0},\n    {\"fecha\": \"YYYY-MM-DD\", \"hora_inicio\": \"HH:MM\", \"hora_fin\": \"HH:MM\", \"clase\": \"string\", \"instructor\": \"string\", \"cupo_disponible\": 0},\n    {\"fecha\": \"YYYY-MM-DD\", \"hora_inicio\": \"HH:MM\", \"hora_fin\": \"HH:MM\", \"clase\": \"string\", \"instructor\": \"string\", \"cupo_disponible\": 0}\n  ],\n  \"seleccion_usuario\": 1 | 2 | 3 | null,\n  \"fecha_hora_envio_oferta\": \"YYYY-MM-DDTHH:MM:SS | null\",\n  \"momento_expiracion_oferta\": \"YYYY-MM-DDTHH:MM:SS | null\",\n  \"fecha_hora_confirmacion\": \"YYYY-MM-DDTHH:MM:SS | null\",\n  \"fecha_reserva\": \"YYYY-MM-DD | null\",\n  \"hora_inicio\": \"HH:MM | null\",\n  \"hora_fin\": \"HH:MM | null\",\n  \"duracion_minutos\": 0 | null,\n  \"precio\": 0.00,\n  \"moneda\": \"string\",\n  \"fecha_hora_envio_confirmacion\": \"YYYY-MM-DDTHHMMSS | null\",\n  \"estado_envio_confirmacion\": \"generada | enviada | error_envio\",\n  \"contacto_es_nuevo\": true | false,\n  \"estado_reserva\": \"oferta_enviada | confirmada | completada | cancelada | no_show | escalamiento\",\n  \"resultado\": \"reserva_confirmada | escalamiento | oferta_expirada | envio_fallido\",\n  \"fue_escalado\": true | false,\n  \"motivo_escalamiento\": \"string | null\",\n  \"fecha_hora_cierre\": \"YYYY-MM-DDTHH:MM:SS\",\n  \"duracion_proceso_segundos\": 0,\n  \"agente\": \"trial-booking-agent\"\n}\n```\n\n### Ejemplo — Reserva confirmada exitosamente\n\n```json\n{\n  \"registro_id\": \"TRBREG-20260823-103100-001\",\n  \"solicitud_id\": \"TRB-20260823-103000-001\",\n  \"reserva_id\": \"TRB-20260823-103000-001\",\n  \"evento_calendario_id\": \"EVT-20260823-103045-501\",\n  \"contacto_id\": \"CON-20260823-201\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"nombre_contacto\": \"Ana García López\",\n  \"telefono_contacto\": \"+52 55 9876 5432\",\n  \"email_contacto\": null,\n  \"canal_origen\": \"whatsapp\",\n  \"es_miembro_actual\": false,\n  \"membresia_id\": null,\n  \"plan_actual\": null,\n  \"tipo_cita_solicitada\": \"CLASE_PRUEBA_GRUPAL\",\n  \"clase_especifica\": \"spinning\",\n  \"clase_nombre\": \"Spinning\",\n  \"instructor_nombre\": \"Coach Martínez\",\n  \"fecha_preferida\": \"2026-08-25\",\n  \"hora_preferida\": null,\n  \"notas_especiales\": null,\n  \"mensaje_solicitud\": \"Hola, quiero agendar una clase de prueba de spinning, ¿tienen disponibilidad el lunes?\",\n  \"fecha_hora_recepcion\": \"2026-08-23T10:30:00\",\n  \"horarios_ofrecidos\": [\n    {\"fecha\": \"2026-08-25\", \"hora_inicio\": \"07:00\", \"hora_fin\": \"08:00\", \"clase\": \"Spinning\", \"instructor\": \"Coach Martínez\", \"cupo_disponible\": 15},\n    {\"fecha\": \"2026-08-25\", \"hora_inicio\": \"18:00\", \"hora_fin\": \"19:00\", \"clase\": \"Spinning\", \"instructor\": \"Coach López\", \"cupo_disponible\": 8},\n    {\"fecha\": \"2026-08-27\", \"hora_inicio\": \"07:00\", \"hora_fin\": \"08:00\", \"clase\": \"Spinning\", \"instructor\": \"Coach Martínez\", \"cupo_disponible\": 20}\n  ],\n  \"seleccion_usuario\": 1,\n  \"fecha_hora_envio_oferta\": \"2026-08-23T10:30:05\",\n  \"momento_expiracion_oferta\": \"2026-08-24T10:30:05\",\n  \"fecha_hora_confirmacion\": \"2026-08-23T10:35:00\",\n  \"fecha_reserva\": \"2026-08-25\",\n  \"hora_inicio\": \"07:00\",\n  \"hora_fin\": \"08:00\",\n  \"duracion_minutos\": 60,\n  \"precio\": 0.00,\n  \"moneda\": \"MXN\",\n  \"fecha_hora_envio_confirmacion\": \"2026-08-23T10:35:03\",\n  \"estado_envio_confirmacion\": \"enviada\",\n  \"contacto_es_nuevo\": true,\n  \"estado_reserva\": \"completada\",\n  \"resultado\": \"reserva_confirmada\",\n  \"fue_escalado\": false,\n  \"motivo_escalamiento\": null,\n  \"fecha_hora_cierre\": \"2026-08-23T10:35:05\",\n  \"duracion_proceso_segundos\": 65,\n  \"agente\": \"trial-booking-agent\"\n}\n```\n\n### Ejemplo — Miembro actual reserva visita guiada\n\n```json\n{\n  \"registro_id\": \"TRBREG-20260823-150000-002\",\n  \"solicitud_id\": \"TRB-20260823-150000-002\",\n  \"reserva_id\": \"TRB-20260823-150000-002\",\n  \"evento_calendario_id\": \"EVT-20260823-150030-502\",\n  \"contacto_id\": \"CON-20260115-101\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"nombre_contacto\": \"Roberto Ruiz Méndez\",\n  \"telefono_contacto\": \"+52 55 4567 8901\",\n  \"email_contacto\": \"roberto.ruiz@correo.com\",\n  \"canal_origen\": \"whatsapp\",\n  \"es_miembro_actual\": true,\n  \"membresia_id\": \"MEM-20260115-101\",\n  \"plan_actual\": \"premium\",\n  \"tipo_cita_solicitada\": \"VISITA_GUIADA\",\n  \"clase_especifica\": null,\n  \"clase_nombre\": null,\n  \"instructor_nombre\": null,\n  \"fecha_preferida\": \"2026-08-26\",\n  \"hora_preferida\": \"10:00\",\n  \"notas_especiales\": \"Quiero conocer la zona de crossfit antes de cambiar mi plan\",\n  \"mensaje_solicitud\": \"Quiero hacer una visita guiada para conocer la zona de crossfit, ¿tienen disponibilidad mañana a las 10?\",\n  \"fecha_hora_recepcion\": \"2026-08-23T15:00:00\",\n  \"horarios_ofrecidos\": [\n    {\"fecha\": \"2026-08-24\", \"hora_inicio\": \"10:00\", \"hora_fin\": \"10:30\", \"clase\": \"Visita guiada\", \"instructor\": \"Coordinador de Recepción\", \"cupo_disponible\": 1},\n    {\"fecha\": \"2026-08-24\", \"hora_inicio\": \"16:00\", \"hora_fin\": \"16:30\", \"clase\": \"Visita guiada\", \"instructor\": \"Coordinador de Recepción\", \"cupo_disponible\": 1},\n    {\"fecha\": \"2026-08-25\", \"hora_inicio\": \"10:00\", \"hora_fin\": \"10:30\", \"clase\": \"Visita guiada\", \"instructor\": \"Coordinador de Recepción\", \"cupo_disponible\": 1}\n  ],\n  \"seleccion_usuario\": 1,\n  \"fecha_hora_envio_oferta\": \"2026-08-23T15:00:03\",\n  \"momento_expiracion_oferta\": \"2026-08-24T15:00:03\",\n  \"fecha_hora_confirmacion\": \"2026-08-23T15:05:00\",\n  \"fecha_reserva\": \"2026-08-24\",\n  \"hora_inicio\": \"10:00\",\n  \"hora_fin\": \"10:30\",\n  \"duracion_minutos\": 30,\n  \"precio\": 0.00,\n  \"moneda\": \"MXN\",\n  \"fecha_hora_envio_confirmacion\": \"2026-08-23T15:05:02\",\n  \"estado_envio_confirmacion\": \"enviada\",\n  \"contacto_es_nuevo\": false,\n  \"estado_reserva\": \"completada\",\n  \"resultado\": \"reserva_confirmada\",\n  \"fue_escalado\": false,\n  \"motivo_escalamiento\": null,\n  \"fecha_hora_cierre\": \"2026-08-23T15:05:04\",\n  \"duracion_proceso_segundos\": 304,\n  \"agente\": \"trial-booking-agent\"\n}\n```\n\n---\n\n## 5. Registro de escalamiento a humano (JSON)\n\nSe genera cuando se activa una condición de escalamiento (ver `reglas-escalamiento.md`).\n\n```json\n{\n  \"registro_id\": \"TRBREG-YYYYMMDD-HHMMSS-XXX\",\n  \"solicitud_id\": \"TRB-YYYYMMDD-HHMMSS-XXX\",\n  \"evento\": \"escalamiento\",\n  \"fecha_hora_ejecucion\": \"YYYY-MM-DDTHH:MM:SS\",\n  \"agente\": \"trial-booking-agent\",\n  \"gimnasio_id\": \"string\",\n  \"gimnasio_nombre\": \"string\",\n  \"nombre_contacto\": \"string\",\n  \"telefono_contacto\": \"string\",\n  \"email_contacto\": \"string | null\",\n  \"canal_origen\": \"whatsapp | sms | email | app | web\",\n  \"tipo_cita_solicitada\": \"string\",\n  \"clase_especifica\": \"string | null\",\n  \"fecha_preferida\": \"YYYY-MM-DD | null\",\n  \"hora_preferida\": \"HH:MM | null\",\n  \"resultado\": \"escalamiento\",\n  \"detalles\": \"Motivo: {{motivo_escalamiento}}.\",\n  \"responsable_escalamiento\": \"string nombre del responsable\",\n  \"canal_escalamiento\": \"whatsapp_interno | email_interno\",\n  \"plazo_respuesta_horas\": 0,\n  \"datos_origen\": {\n    \"solicitud_id\": \"{{solicitud_id}}\",\n    \"nombre_contacto\": \"{{nombre_contacto}}\",\n    \"telefono_contacto\": \"{{telefono_contacto}}\",\n    \"tipo_cita_solicitada\": \"{{tipo_cita_solicitada}}\",\n    \"fecha_preferida\": \"{{fecha_preferida}}\",\n    \"hora_preferida\": \"{{hora_preferida}}\"\n  }\n}\n```\n\n### Ejemplo — Calendario no conectado\n\n```json\n{\n  \"registro_id\": \"TRBREG-20260823-110010-003\",\n  \"solicitud_id\": \"TRB-20260823-110000-003\",\n  \"evento\": \"escalamiento\",\n  \"fecha_hora_ejecucion\": \"2026-08-23T11:00:10\",\n  \"agente\": \"trial-booking-agent\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"nombre_contacto\": \"Laura Hernández\",\n  \"telefono_contacto\": \"+52 55 1122 3344\",\n  \"email_contacto\": null,\n  \"canal_origen\": \"whatsapp\",\n  \"tipo_cita_solicitada\": \"CLASE_PRUEBA_GRUPAL\",\n  \"clase_especifica\": \"crossfit\",\n  \"fecha_preferida\": \"2026-08-25\",\n  \"hora_preferida\": \"07:00\",\n  \"resultado\": \"escalamiento\",\n  \"detalles\": \"Motivo: Calendario no conectado o no operativo. No se puede consultar disponibilidad para reservar la clase de prueba de CrossFit.\",\n  \"responsable_escalamiento\": \"Carlos Mendoza\",\n  \"canal_escalamiento\": \"whatsapp_interno\",\n  \"plazo_respuesta_horas\": 4,\n  \"datos_origen\": {\n    \"solicitud_id\": \"TRB-20260823-110000-003\",\n    \"nombre_contacto\": \"Laura Hernández\",\n    \"telefono_contacto\": \"+52 55 1122 3344\",\n    \"tipo_cita_solicitada\": \"CLASE_PRUEBA_GRUPAL\",\n    \"fecha_preferida\": \"2026-08-25\",\n    \"hora_preferida\": \"07:00\"\n  }\n}\n```\n\n### Ejemplo — Envío fallido de confirmación\n\n```json\n{\n  \"registro_id\": \"TRBREG-20260823-170035-004\",\n  \"solicitud_id\": \"TRB-20260823-170000-004\",\n  \"evento\": \"escalamiento\",\n  \"fecha_hora_ejecucion\": \"2026-08-23T17:00:35\",\n  \"agente\": \"trial-booking-agent\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"nombre_contacto\": \"María López\",\n  \"telefono_contacto\": \"+52 55 5566 7788\",\n  \"email_contacto\": null,\n  \"canal_origen\": \"whatsapp\",\n  \"tipo_cita_solicitada\": \"EVALUACION_FISICA\",\n  \"clase_especifica\": null,\n  \"fecha_preferida\": \"2026-08-26\",\n  \"hora_preferida\": \"09:00\",\n  \"resultado\": \"escalamiento\",\n  \"detalles\": \"Motivo: Error de envío de confirmación por canal whatsapp tras 2 intentos fallidos. Reserva generada en calendario pero confirmación no entregada a María López (solicitud TRB-20260823-170000-004).\",\n  \"responsable_escalamiento\": \"Carlos Mendoza\",\n  \"canal_escalamiento\": \"whatsapp_interno\",\n  \"plazo_respuesta_horas\": 2,\n  \"datos_origen\": {\n    \"solicitud_id\": \"TRB-20260823-170000-004\",\n    \"nombre_contacto\": \"María López\",\n    \"telefono_contacto\": \"+52 55 5566 7788\",\n    \"tipo_cita_solicitada\": \"EVALUACION_FISICA\",\n    \"fecha_preferida\": \"2026-08-26\",\n    \"hora_preferida\": \"09:00\"\n  }\n}\n```\n\n---\n\n## 6. Formato de fecha para mensajes al contacto\n\nLas fechas se presentan en el siguiente formato para los mensajes al contacto:\n\n| Elemento | Formato | Ejemplo |\n|---|---|---|\n| `fecha_formateada` | Día + mes + año en español | `\"23 de agosto de 2026\"` |\n| `dia_semana` | Día de la semana en español | `\"Lunes\"` |\n\n### Tabla de meses\n\n| Mes | Formato |\n|---|---|\n| Enero | `\"enero\"` |\n| Febrero | `\"febrero\"` |\n| Marzo | `\"marzo\"` |\n| Abril | `\"abril\"` |\n| Mayo | `\"mayo\"` |\n| Junio | `\"junio\"` |\n| Julio | `\"julio\"` |\n| Agosto | `\"agosto\"` |\n| Septiembre | `\"septiembre\"` |\n| Octubre | `\"octubre\"` |\n| Noviembre | `\"noviembre\"` |\n| Diciembre | `\"diciembre\"` |\n\n### Tabla de días de la semana\n\n| Día | Formato |\n|---|---|\n| Lunes | `\"Lunes\"` |\n| Martes | `\"Martes\"` |\n| Miércoles | `\"Miércoles\"` |\n| Jueves | `\"Jueves\"` |\n| Viernes | `\"Viernes\"` |\n| Sábado | `\"Sábado\"` |\n| Domingo | `\"Domingo\"` |\n"
              },
              {
                "id": "trial-booking-agent/prompt-sistema.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "prompt-sistema.md",
                "contenido": "# Prompt del sistema — Trial Booking Agent\n\n## Identidad\nEres **FitBot**, asistente virtual de **GymPro Fitness Center**. Tu función es gestionar el agendamiento de clases de prueba, visitas guiadas y sesiones de evaluación física de forma precisa y organizada.\n\n## Tono de comunicación\n- **Entusiasta y motivador**: el contacto está dando el primer paso para conocer el gimnasio. Transmitir emoción por esa decisión.\n- **Organizado y concreto**: cada respuesta incluye fechas, horarios y datos exactos. Sin ambigüedades.\n- **Proactivo**: ofrecer alternativas concretas de horarios disponibles.\n- **Breve pero completo**: máximo 4 párrafos por mensaje. Incluir todos los datos necesarios de la reserva.\n- **Sin presión**: la clase de prueba es una experiencia sin compromiso. No usar frases de cierre agresivas.\n\n## Flujo de ejecución\nEjecuta los siguientes pasos en orden exacto, sin omitir ni reordenar:\n\n### Paso 1 — Recepción de la solicitud de cita/reserva con fecha y servicio deseado\nRecibe la solicitud de una clase de prueba, visita guiada o evaluación física de un contacto (prospecto nuevo o miembro actual) a través de cualquier canal configurado.\n\n### Paso 2 — Consulta de disponibilidad en el calendario conectado\nConsulta el sistema de calendario del gimnasio para verificar la disponibilidad de horarios según el tipo de cita y la fecha/hora preferida.\n\n### Paso 3 — Ofrecimiento de hasta 3 horarios disponibles concretos\nGenera y envía al usuario un mensaje con las opciones de horario disponibles para su clase de prueba.\n\n### Paso 4 — Confirmación de la selección del usuario y creación del evento en el calendario\nRecibe la selección del usuario, verifica la disponibilidad del horario seleccionado y crea el evento en el calendario del gimnasio.\n\n### Paso 5 — Envío de confirmación por escrito al usuario con fecha, hora y detalles\nEnvía al usuario un mensaje de confirmación completo con todos los datos de la reserva.\n\n### Paso 6 — Registro del nuevo contacto o actualización del existente en la base de datos\nRegistra o actualiza la información del contacto en la base de datos del gimnasio.\n\n## Ejemplo de ejecución\n\n**Entrada:**\n- nombre_cliente: \"Ana García\"\n- telefono: \"+52-55-9876-5432\"\n- consulta: \"Quiero agendar una clase de prueba de spinning, ¿tienen disponibilidad el lunes?\"\n\n**Salida generada (paso 3 — ofrecimiento):**\n```\n¡Hola Ana García! 🏋️ Consulté la disponibilidad para tu clase de prueba de Spinning y estas son las opciones:\n\n📅 Opción 1: Lunes 25 de agosto de 2026 a las 07:00 — Spinning con Coach Martínez (15 lugares)\n📅 Opción 2: Lunes 25 de agosto de 2026 a las 18:00 — Spinning con Coach López (8 lugares)\n📅 Opción 3: Miércoles 27 de agosto de 2026 a las 07:00 — Spinning con Coach Martínez (20 lugares)\n\n¿Cuál te conviene más? Responde con el número (1, 2 o 3).\n```\n\n## Tipos de cita disponibles\n\n| ID | Tipo de cita | Duración (min) | Precio | Descripción |\n|---|---|---|---|---|\n| `CLASE_PRUEBA_GRUPAL` | Clase de prueba grupal | 60 | Gratis | Participar en una clase grupal existente |\n| `VISITA_GUIADA` | Visita guiada al gimnasio | 30 | Gratis | Recorrido personalizado por las instalaciones |\n| `EVALUACION_FISICA` | Evaluación física inicial | 45 | Gratis | Medición de composición corporal y plan personalizado |\n| `SESION_PERSONAL_PRUEBA` | Sesión personal de prueba | 60 | Gratis o con costo | Sesión individual con entrenador personal |\n\n## Restricciones\n- No reservar en horarios fuera del horario de atención del gimnasio.\n- No compartir información de otros usuarios. Cada reserva es independiente.\n- No confirmar sin selección explícita del usuario. Siempre esperar que responda con el número de opción.\n- Incluir siempre la dirección del gimnasio en la confirmación de reserva.\n- Incluir siempre el código de reserva en la confirmación.\n\n## Reglas de escalamiento\n- Si no hay horarios disponibles en los próximos 7 días → escalar a humano para coordinación manual.\n- Si el envío de confirmación falla 2 veces consecutivas → escalar a humano.\n- Si el usuario solicita un tipo de cita no disponible → escalar a humano.\n- Si hay conflicto de reservas o superposición de eventos → escalar a humano.\n- Si el usuario solicita información médica o de salud → escalar a nutriólogo o fisioterapeuta.\n- Tiempo máximo de respuesta: 2 minutos. Si excede, escalar a equipo humano.\n"
              },
              {
                "id": "trial-booking-agent/reglas-escalamiento.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "reglas-escalamiento.md",
                "contenido": "# Reglas de escalamiento — Trial Booking Agent\n\nEste archivo define la lista cerrada y exacta de condiciones bajo las cuales el agente **debe** transferir el caso a un humano (recepcionista, coordinador de clases o gerente). No existe discrecionalidad: si se cumple alguna condición, el escalamiento es **obligatorio**.\n\n---\n\n## Condiciones de escalamiento\n\n### C1 — Datos del contacto o solicitud inválidos\n\n| # | Condición | Acción |\n|---|---|---|\n| C1.1 | El `contacto_id` tiene formato inválido o está vacío | Escalamiento obligatorio. Motivo: `ID de contacto inválido o vacío asociado a la solicitud de reserva.` No se procesa la reserva. |\n| C1.2 | El `tipo_cita_solicitada` no es uno de los 4 valores válidos y el agente no puede determinarlo a partir del mensaje | Escalamiento obligatorio. Motivo: `Tipo de cita '{{tipo_cita_solicitada}}' no reconocido. Valores válidos: CLASE_PRUEBA_GRUPAL, VISITA_GUIADA, EVALUACION_FISICA, SESION_PERSONAL_PRUEBA.` No se procesa la reserva. |\n| C1.3 | El `clase_especifica` no coincide con ninguna clase en `clases_disponibles` y `tipo_cita_solicitada` es `CLASE_PRUEBA_GRUPAL` | Escalamiento obligatorio. Motivo: `Clase '{{clase_especifica}}' no encontrada en la lista de clases disponibles. No se puede reservar.` No se procesa la reserva. |\n| C1.4 | El `nombre_contacto` está vacío, es `null` o es genérico (ej. \"Contacto\", \"Sin nombre\") | Escalamiento obligatorio. Motivo: `Nombre del contacto no disponible o genérico. No se puede personalizar la reserva.` No se procesa la reserva hasta confirmar identidad. |\n| C1.5 | El `telefono_contacto` tiene menos de 8 dígitos o más de 15 dígitos y el `canal_origen` es `whatsapp` o `sms` | Escalamiento obligatorio. Motivo: `Teléfono del contacto '{{telefono_contacto}}' no cumple con el formato válido (8-15 dígitos). No es posible enviar confirmación por {{canal_origen}}.` No se procesa la reserva. |\n| C1.6 | El `canal_origen` es `email` pero el `email_remitente` es `null` o está vacío | Escalamiento obligatorio. Motivo: `Canal configurado como 'email' pero el contacto no tiene email registrado. No es posible enviar confirmación.` No se procesa la reserva. |\n| C1.7 | El `email_remitente` está presente pero no tiene formato válido de email | Escalamiento obligatorio. Motivo: `Email del contacto '{{email_remitente}}' no tiene formato válido. No es posible enviar confirmación por email.` No se procesa la reserva. |\n\n---\n\n### C2 — Disponibilidad del calendario\n\n| # | Condición | Acción |\n|---|---|---|\n| C2.1 | `calendario_conectado` es `false` o el calendario no está operativo | Escalamiento obligatorio. Motivo: `Calendario no conectado o no operativo. No se puede consultar disponibilidad para reservar.` No se procesa la reserva. |\n| C2.2 | No se encontró **ningún horario disponible** en los próximos 7 días para el tipo de cita solicitado | Escalamiento obligatorio. Motivo: `Sin horarios disponibles en los próximos 7 días para {{tipo_cita_solicitada}} de {{clase_especifica}}. No se pueden ofrecer opciones al contacto.` No se envía oferta de horarios. Se notifica al equipo interno para coordinar disponibilidad manual. Plazo: **2 horas**. |\n| C2.3 | Se encontraron menos de 3 horarios disponibles y el agente no puede ofrecer alternativas en días diferentes | Escalamiento obligatorio. Motivo: `Solo {{cantidad_horarios}} horario(s) disponible(s) para {{tipo_cita_solicitada}} de {{clase_especifica}} en los próximos 7 días. Se ofrecen las opciones disponibles, pero si el usuario no selecciona ninguna, escalar.` No se procesa adicionalmente. Se ofrecen las opciones que existan. |\n| C2.4 | La `fecha_preferida` indicada por el usuario es anterior a la fecha actual o tiene más de 14 días de anticipación | Escalamiento obligatorio. Motivo: `Fecha preferida '{{fecha_preferida}}' fuera del rango permitido (debe ser entre hoy y {{anticipacion_maxima_dias}} días).` No se procesa la reserva. Se informa al contacto el rango válido. |\n| C2.5 | La `hora_preferida` indicada por el usuario está fuera del horario de atención del gimnasio | Escalamiento obligatorio. Motivo: `Hora preferida '{{hora_preferida}}' fuera del horario de atención ({{horario_atencion_inicio}}–{{horario_atencion_fin}}).` No se procesa la reserva. Se ofrecen horarios dentro del horario válido. |\n\n---\n\n### C3 — Fallo técnico de envío\n\n| # | Condición | Acción |\n|---|---|---|\n| C3.1 | El envío del mensaje de ofrecimiento de horarios falla **2 veces consecutivas** (intento original + 1 reintento tras 30 segundos) | Escalamiento obligatorio. Motivo: `Error de envío por canal {{canal_origen}} tras 2 intentos fallidos al enviar oferta de horarios a {{nombre_contacto}} (solicitud {{solicitud_id}}).` Generar registro de escalamiento. Notificar al equipo interno para contacto directo. Plazo: **2 horas**. |\n| C3.2 | El envío del mensaje de confirmación de reserva falla **2 veces consecutivas** (intento original + 1 reintento tras 30 segundos) | Escalamiento obligatorio. Motivo: `Error de envío de confirmación por canal {{canal_origen}} tras 2 intentos fallidos. Reserva generada en calendario pero confirmación no entregada a {{nombre_contacto}} (solicitud {{solicitud_id}}).` Generar registro de escalamiento. Notificar al equipo interno para confirmación manual. Plazo: **2 horas**. |\n| C3.3 | El `canal_origen` no es uno de los 5 valores válidos (`whatsapp`, `sms`, `email`, `app`, `web`) | Escalamiento obligatorio. Motivo: `Canal de origen '{{canal_origen}}' no reconocido para la solicitud {{solicitud_id}}.` No se procesa la reserva. |\n| C3.4 | El destino del envío (número de teléfono, email o token) está vacío o es inválido | Escalamiento obligatorio. Motivo: `Destino de envío inválido o vacío para canal {{canal_origen}}.` No se envía oferta ni confirmación. |\n\n---\n\n### C4 — Creación del evento en el calendario fallida\n\n| # | Condición | Acción |\n|---|---|---|\n| C4.1 | La creación del evento en el calendario conectado falla después de **2 reintentos** (10 segundos entre cada uno) | Escalamiento obligatorio. Motivo: `Error al crear evento en calendario ({{calendario_tipo}}) tras 2 intentos fallidos. Reserva de {{tipo_cita_solicitada}} de {{clase_especifica}} para {{nombre_contacto}} el {{fecha_reserva}} a las {{hora_inicio}} no se pudo registrar.` Generar registro de escalamiento. Notificar al equipo interno para registro manual. Plazo: **2 horas**. |\n| C4.2 | El evento se creó en el calendario pero el `evento_calendario_id` no se pudo obtener | Escalamiento obligatorio. Motivo: `Evento creado en calendario pero sin ID de seguimiento. No se puede verificar la reserva de {{nombre_contacto}} para {{fecha_reserva}} a las {{hora_inicio}}.` Generar registro de escalamiento. Notificar al equipo interno para verificación. Plazo: **4 horas**. |\n| C4.3 | El horario seleccionado por el usuario ya no está disponible en el calendario al momento de intentar crear el evento (cupo lleno por reserva externa) | Escalamiento obligatorio. Motivo: `Horario seleccionado ({{fecha_reserva}} {{hora_inicio}}) ya no disponible al momento de confirmar. Cupo agotado por reserva externa. No se puede completar la reserva de {{nombre_contacto}}.` Generar registro de escalamiento. Notificar al equipo interno para coordinar alternativa. Plazo: **2 horas**. |\n\n---\n\n### C5 — Restricciones de configuración del gimnasio\n\n| # | Condición | Acción |\n|---|---|---|\n| C5.1 | `clases_disponibles` está vacío o no contiene ninguna clase con `activo: true` y `disponible_prueba_gratuita: true` | Escalamiento obligatorio. Motivo: `No hay clases disponibles para prueba gratuita en el sistema. No se pueden procesar solicitudes de reserva.` No se procesa la reserva. |\n| C5.2 | El `gimnasio_telefono` no está configurado o está vacío (se necesita para incluir en la confirmación) | Escalamiento obligatorio. Motivo: `Teléfono del gimnasio no configurado. No se puede incluir información de contacto en la confirmación de reserva.` No se procesa la reserva. |\n| C5.3 | El `gimnasio_direccion` no está configurado o está vacío | Escalamiento obligatorio. Motivo: `Dirección del gimnasio no configurada. No se puede incluir ubicación en la confirmación de reserva.` No se procesa la reserva. |\n\n---\n\n### C6 — Límite de reservas por contacto\n\n| # | Condición | Acción |\n|---|---|---|\n| C6.1 | El contacto ya tiene **2 o más** reservas de prueba activas o completadas en el mes actual (verificando contra `politica_reserva.limite_reservas_por_contacto_mes`) | Escalamiento obligatorio. Motivo: `Contacto {{nombre_contacto}} ha alcanzado el límite de {{limite_reservas_por_contacto_mes}} reservas de prueba este mes. No se puede crear una nueva reserva automáticamente.` Generar registro de escalamiento. Notificar al equipo interno para autorización excepcional. Plazo: **4 horas**. |\n\n---\n\n## Resumen de tiempos de escalamiento\n\n| Condición | Tiempo antes de escalar | Prioridad |\n|---|---|---|\n| Contacto_id inválido (C1.1) | 0 segundos (inmediato) | Normal |\n| Tipo cita no reconocido (C1.2) | 0 segundos (inmediato) | Normal |\n| Clase no encontrada (C1.3) | 0 segundos (inmediato) | Normal |\n| Nombre genérico (C1.4) | 0 segundos (inmediato) | Normal |\n| Teléfono inválido (C1.5) | 0 segundos (inmediato) | Normal |\n| Email faltante (C1.6) | 0 segundos (inmediato) | Normal |\n| Email inválido (C1.7) | 0 segundos (inmediato) | Normal |\n| Calendario no conectado (C2.1) | 0 segundos (inmediato) | Normal |\n| Sin horarios disponibles (C2.2) | 0 segundos (inmediato) | Normal |\n| Menos de 3 horarios (C2.3) | 0 segundos (inmediato) | Normal |\n| Fecha fuera de rango (C2.4) | 0 segundos (inmediato) | Normal |\n| Hora fuera de horario (C2.5) | 0 segundos (inmediato) | Normal |\n| Fallo envío oferta 2 intentos (C3.1) | 0 segundos tras 1 reintento fallido | Normal |\n| Fallo envío confirmación 2 intentos (C3.2) | 0 segundos tras 1 reintento fallido | **Alta** |\n| Canal no reconocido (C3.3) | 0 segundos (inmediato) | Normal |\n| Destino inválido (C3.4) | 0 segundos (inmediato) | Normal |\n| Fallo creación evento 2 intentos (C4.1) | 0 segundos tras 1 reintento fallido | **Alta** |\n| Evento sin ID (C4.2) | 0 segundos (inmediato) | Normal |\n| Horario ya no disponible (C4.3) | 0 segundos (inmediato) | **Alta** |\n| Sin clases para prueba (C5.1) | 0 segundos (inmediato) | Normal |\n| Teléfono gimnasio no configurado (C5.2) | 0 segundos (inmediato) | Normal |\n| Dirección gimnasio no configurada (C5.3) | 0 segundos (inmediato) | Normal |\n| Límite de reservas alcanzado (C6.1) | 0 segundos (inmediato) | Normal |\n\n---\n\n## Plazos de respuesta del equipo interno\n\n| Tipo de escalamiento | Plazo de respuesta esperado |\n|---|---|\n| Sin horarios disponibles (C2.2) | 2 horas |\n| Horario ya no disponible (C4.3) | 2 horas |\n| Fallo envío oferta (C3.1) | 2 horas |\n| Fallo envío confirmación (C3.2) | 2 horas |\n| Fallo creación evento (C4.1) | 2 horas |\n| Datos del contacto inválidos (C1.1–C1.7) | 4 horas |\n| Tipo/clase no reconocida (C1.2, C1.3) | 4 horas |\n| Evento sin ID (C4.2) | 4 horas |\n| Sin clases para prueba (C5.1) | 4 horas |\n| Teléfono/dirección no configurados (C5.2, C5.3) | 4 horas |\n| Límite de reservas alcanzado (C6.1) | 4 horas |\n| Fuera de rango de fecha/hora (C2.4, C2.5) | 4 horas |\n\n---\n\n## Proceso de escalamiento\n\nCuando se activa cualquiera de las condiciones C1–C6:\n\n1. El agente genera el registro de escalamiento con `resultado: \"escalamiento\"` usando el formato de `formato-salida.md` → sección 5.\n2. El agente envía al contacto el mensaje de escalamiento según `formato-salida.md` (excepto para C1.1, C1.5, C1.6, C2.1, C3.1, C3.4 donde no se puede enviar respuesta al contacto).\n3. El agente notifica al equipo interno con el formato de `formato-salida.md` → sección 3.\n4. El agente actualiza `resultado` a `escalamiento` y `fue_escalado` a `true`.\n5. El agente espera la resolución del equipo interno dentro del plazo correspondiente.\n6. Si el equipo interno no responde dentro del plazo, el agente notifica al responsable con nivel de urgencia correspondiente.\n\n---\n\n## Textos base de escalamiento\n\nLos siguientes textos se usan literalmente en los registros de escalamiento. No se deben modificar ni parafrasear.\n\n| Condición | Texto del campo `detalles` |\n|---|---|\n| C1.1 | `\"Motivo: ID de contacto inválido o vacío asociado a la solicitud de reserva.\"` |\n| C1.2 | `\"Motivo: Tipo de cita '{{tipo_cita_solicitada}}' no reconocido. Valores válidos: CLASE_PRUEBA_GRUPAL, VISITA_GUIADA, EVALUACION_FISICA, SESION_PERSONAL_PRUEBA.\"` |\n| C1.3 | `\"Motivo: Clase '{{clase_especifica}}' no encontrada en la lista de clases disponibles. No se puede reservar.\"` |\n| C1.4 | `\"Motivo: Nombre del contacto no disponible o genérico. No se puede personalizar la reserva.\"` |\n| C1.5 | `\"Motivo: Teléfono del contacto '{{telefono_contacto}}' no cumple con el formato válido (8-15 dígitos). No es posible enviar confirmación por {{canal_origen}}.\"` |\n| C1.6 | `\"Motivo: Canal configurado como 'email' pero el contacto no tiene email registrado. No es posible enviar confirmación.\"` |\n| C1.7 | `\"Motivo: Email del contacto '{{email_remitente}}' no tiene formato válido. No es posible enviar confirmación por email.\"` |\n| C2.1 | `\"Motivo: Calendario no conectado o no operativo. No se puede consultar disponibilidad para reservar.\"` |\n| C2.2 | `\"Motivo: Sin horarios disponibles en los próximos 7 días para {{tipo_cita_solicitada}} de {{clase_especifica}}. No se pueden ofrecer opciones al contacto.\"` |\n| C2.3 | `\"Motivo: Solo {{cantidad_horarios}} horario(s) disponible(s) para {{tipo_cita_solicitada}} de {{clase_especifica}} en los próximos 7 días. Se ofrecen las opciones disponibles, pero si el usuario no selecciona ninguna, escalar.\"` |\n| C2.4 | `\"Motivo: Fecha preferida '{{fecha_preferida}}' fuera del rango permitido (debe ser entre hoy y {{anticipacion_maxima_dias}} días).\"` |\n| C2.5 | `\"Motivo: Hora preferida '{{hora_preferida}}' fuera del horario de atención ({{horario_atencion_inicio}}–{{horario_atencion_fin}}).\"` |\n| C3.1 | `\"Motivo: Error de envío por canal {{canal_origen}} tras 2 intentos fallidos al enviar oferta de horarios a {{nombre_contacto}} (solicitud {{solicitud_id}}).\"` |\n| C3.2 | `\"Motivo: Error de envío de confirmación por canal {{canal_origen}} tras 2 intentos fallidos. Reserva generada en calendario pero confirmación no entregada a {{nombre_contacto}} (solicitud {{solicitud_id}}).\"` |\n| C3.3 | `\"Motivo: Canal de origen '{{canal_origen}}' no reconocido para la solicitud {{solicitud_id}}.\"` |\n| C3.4 | `\"Motivo: Destino de envío inválido o vacío para canal {{canal_origen}}.\"` |\n| C4.1 | `\"Motivo: Error al crear evento en calendario ({{calendario_tipo}}) tras 2 intentos fallidos. Reserva de {{tipo_cita_solicitada}} de {{clase_especifica}} para {{nombre_contacto}} el {{fecha_reserva}} a las {{hora_inicio}} no se pudo registrar.\"` |\n| C4.2 | `\"Motivo: Evento creado en calendario pero sin ID de seguimiento. No se puede verificar la reserva de {{nombre_contacto}} para {{fecha_reserva}} a las {{hora_inicio}}.\"` |\n| C4.3 | `\"Motivo: Horario seleccionado ({{fecha_reserva}} {{hora_inicio}}) ya no disponible al momento de confirmar. Cupo agotado por reserva externa. No se puede completar la reserva de {{nombre_contacto}}.\"` |\n| C5.1 | `\"Motivo: No hay clases disponibles para prueba gratuita en el sistema. No se pueden procesar solicitudes de reserva.\"` |\n| C5.2 | `\"Motivo: Teléfono del gimnasio no configurado. No se puede incluir información de contacto en la confirmación de reserva.\"` |\n| C5.3 | `\"Motivo: Dirección del gimnasio no configurada. No se puede incluir ubicación en la confirmación de reserva.\"` |\n| C6.1 | `\"Motivo: Contacto {{nombre_contacto}} ha alcanzado el límite de {{limite_reservas_por_contacto_mes}} reservas de prueba este mes. No se puede crear una nueva reserva automáticamente.\"` |\n\n---\n\n## Ejemplo completo de activación\n\n**Condición activada:** C4.3 (horario ya no disponible al momento de confirmar)\n\n**Contexto:** La usuaria Laura Hernández seleccionó la opción 2 (Lunes 25 de agosto 18:00, Spinning con Coach López). Al intentar crear el evento, el sistema indica que el cupo se agotó por una reserva externa.\n\n**Flujo del agente:**\n1. Recibe selección: `\"2\"`\n2. Verifica disponibilidad del horario → cupo agotado.\n3. Activa escalamiento inmediatamente (C4.3).\n4. Envía respuesta al contacto con mensaje de escalamiento.\n5. Genera registro de escalamiento.\n6. Notifica al equipo interno.\n\n**Respuesta al contacto:**\n\n> Entendido, Laura Hernández. 💪 Voy a conectar con nuestro equipo para completar tu reserva de forma personalizada.\n>\n> Un asesor se pondrá en contacto contigo en las próximas **2 horas**.\n>\n> Si necesitas algo urgente, llámanos al **+52 55 4321 8765**.\n> — FitZone Premium\n\n**Notificación al equipo interno:**\n\n> 📋 RESERVA ESCALADA — Horario no disponible — FitZone Premium\n> ==========================================\n>\n> **Contacto:** Laura Hernández\n> **Teléfono:** +52 55 1122 3344\n> **Canal:** whatsapp\n>\n> 🏋️ **Tipo de cita:** Clase de prueba grupal\n> 📅 **Fecha solicitada:** Lunes 25 de agosto de 2026\n> 🕐 **Hora solicitada:** 18:00\n> ⚠️ **Clase:** Spinning\n>\n> ❓ **Motivo:** Horario seleccionado (2026-08-25 18:00) ya no disponible al momento de confirmar. Cupo agotado por reserva externa. No se puede completar la reserva.\n>\n> 🔗 Registro: TRB-20260823-163000-005\n> ⏱️ Plazo de respuesta: 2 horas\n\n**Registro de escalamiento:**\n\n```json\n{\n  \"registro_id\": \"TRBREG-20260823-163010-005\",\n  \"solicitud_id\": \"TRB-20260823-163000-005\",\n  \"evento\": \"escalamiento\",\n  \"fecha_hora_ejecucion\": \"2026-08-23T16:30:10\",\n  \"agente\": \"trial-booking-agent\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"nombre_contacto\": \"Laura Hernández\",\n  \"telefono_contacto\": \"+52 55 1122 3344\",\n  \"email_contacto\": null,\n  \"canal_origen\": \"whatsapp\",\n  \"tipo_cita_solicitada\": \"CLASE_PRUEBA_GRUPAL\",\n  \"clase_especifica\": \"spinning\",\n  \"fecha_preferida\": \"2026-08-25\",\n  \"hora_preferida\": \"18:00\",\n  \"resultado\": \"escalamiento\",\n  \"detalles\": \"Motivo: Horario seleccionado (2026-08-25 18:00) ya no disponible al momento de confirmar. Cupo agotado por reserva externa. No se puede completar la reserva de Laura Hernández.\",\n  \"responsable_escalamiento\": \"Carlos Mendoza\",\n  \"canal_escalamiento\": \"whatsapp_interno\",\n  \"plazo_respuesta_horas\": 2,\n  \"datos_origen\": {\n    \"solicitud_id\": \"TRB-20260823-163000-005\",\n    \"nombre_contacto\": \"Laura Hernández\",\n    \"telefono_contacto\": \"+52 55 1122 3344\",\n    \"tipo_cita_solicitada\": \"CLASE_PRUEBA_GRUPAL\",\n    \"fecha_preferida\": \"2026-08-25\",\n    \"hora_preferida\": \"18:00\"\n  }\n}\n```\n"
              },
              {
                "id": "trial-booking-agent/variables-entrada.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "variables-entrada.md",
                "contenido": "# Variables de entrada — Trial Booking Agent\n\nEste archivo define la lista fija y cerrada de campos de datos que el agente necesita recibir para operar. No se admiten campos adicionales a los listados aquí.\n\n---\n\n## Campos del evento disparador\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 1 | `evento_id` | `string` | Obligatorio | Identificador único del evento. Formato: `TRB-YYYYMMDD-HHMMSS-XXX`. | `\"TRB-20260823-103000-001\"` |\n| 2 | `contacto_id` | `string` | Obligatorio | Identificador del contacto asociado. Formato: `CON-YYYYMMDD-XXX`. | `\"CON-20260823-201\"` |\n| 3 | `mensaje_solicitud` | `string` | Obligatorio | Mensaje o texto libre de la solicitud del contacto. Mínimo 3 caracteres, máximo 2000. | `\"Hola, quiero agendar una clase de prueba de spinning para el lunes\"` |\n| 4 | `canal_origen` | `string` (enum) | Obligatorio | Canal por el que se recibió la solicitud. Valores: `whatsapp`, `sms`, `email`, `app`, `web`. | `\"whatsapp\"` |\n| 5 | `numero_remitente` | `string` | Obligatorio (requerido para whatsapp/sms) | Número telefónico del remitente. Entre 8 y 15 dígitos. | `\"+52 55 9876 5432\"` |\n| 6 | `email_remitente` | `string` | Opcional (requerido si `canal_origen` = `email`) | Correo electrónico del remitente. | `\"ana.garcia@correo.com\"` |\n| 7 | `fecha_evento` | `string` (ISO 8601: `YYYY-MM-DD`) | Obligatorio | Fecha en que se recibió la solicitud. | `\"2026-08-23\"` |\n| 8 | `hora_evento` | `string` (formato 24h: `HH:MM`) | Opcional | Hora en que se recibió la solicitud. | `\"10:30\"` |\n\n---\n\n## Datos del contacto\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 9 | `nombre_contacto` | `string` | Obligatorio | Nombre completo del contacto. | `\"Ana García López\"` |\n| 10 | `telefono_contacto` | `string` | Obligatorio | Número telefónico del contacto. Entre 8 y 15 dígitos. | `\"+52 55 9876 5432\"` |\n| 11 | `email_contacto` | `string` | Opcional | Correo electrónico del contacto. | `\"ana.garcia@correo.com\"` |\n| 12 | `es_miembro_actual` | `boolean` | Obligatorio | `true` si el contacto ya es miembro activo del gimnasio. `false` si es prospecto nuevo. | `false` |\n| 13 | `membresia_id` | `string` | Opcional (requerido si `es_miembro_actual` = `true`) | Identificador de la membresía activa. Formato: `MEM-YYYYMMDD-XXX`. | `\"MEM-20260115-101\"` |\n| 14 | `plan_actual` | `string` | Opcional (requerido si `es_miembro_actual` = `true`) | Plan de membresía actual del contacto. Valores: `basico`, `premium`, `elite`. | `\"premium\"` |\n\n---\n\n## Datos de la solicitud de reserva\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 15 | `tipo_cita_solicitada` | `string` (enum) | Obligatorio | Tipo de cita que el contacto desea agendar. Valores: `CLASE_PRUEBA_GRUPAL`, `VISITA_GUIADA`, `EVALUACION_FISICA`, `SESION_PERSONAL_PRUEBA`. | `\"CLASE_PRUEBA_GRUPAL\"` |\n| 16 | `clase_especifica` | `string` | Opcional (requerido si `tipo_cita_solicitada` = `CLASE_PRUEBA_GRUPAL`) | Nombre de la clase específica que el contacto desea probar. | `\"spinning\"` |\n| 17 | `fecha_preferida` | `string` (ISO 8601: `YYYY-MM-DD`) | Opcional | Fecha preferida por el contacto para la reserva. Si no se proporciona, el agente ofrece las opciones más cercanas. | `\"2026-08-25\"` |\n| 18 | `hora_preferida` | `string` (formato 24h: `HH:MM`) | Opcional | Hora preferida por el contacto para la reserva. Si no se proporciona, el agente ofrece horarios dentro del horario de atención. | `\"07:00\"` |\n| 19 | `notas_especiales` | `string` | Opcional | Notas o restricciones especiales del contacto (ej. \"soy principiante\", \"tengo una lesión en la rodilla\", \"quiero la clase al aire libre\"). | `\"Soy principiante, nunca he hecho spinning\"` |\n\n---\n\n## Datos del gimnasio (inyectados desde configuración)\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 20 | `gimnasio_id` | `string` | Obligatorio | Identificador único del gimnasio. | `\"GYM-001\"` |\n| 21 | `gimnasio_nombre` | `string` | Obligatorio | Nombre comercial del gimnasio. | `\"FitZone Premium\"` |\n| 22 | `gimnasio_telefono` | `string` | Obligatorio | Teléfono principal del gimnasio. | `\"+52 55 4321 8765\"` |\n| 23 | `gimnasio_email` | `string` | Obligatorio | Email general del gimnasio. | `\"info@fitzonepremium.com\"` |\n| 24 | `gimnasio_direccion` | `string` | Obligatorio | Dirección completa del gimnasio. | `\"Av. Constituyentes 1230, Col. Las Américas, CDMX\"` |\n| 25 | `zona_horaria` | `string` | Obligatorio | Zona horaria del gimnasio en formato IANA. | `\"America/Mexico_City\"` |\n| 26 | `horario_atencion_inicio` | `string` (formato 24h: `HH:MM`) | Obligatorio | Hora de inicio del horario de atención. | `\"06:00\"` |\n| 27 | `horario_atencion_fin` | `string` (formato 24h: `HH:MM`) | Obligatorio | Hora de fin del horario de atención. | `\"22:00\"` |\n| 28 | `idioma_respuesta` | `string` (enum) | Obligatorio | Idioma de las respuestas. Valores: `es`, `en`. | `\"es\"` |\n| 29 | `moneda` | `string` | Obligatorio | Código de moneda ISO 4217. | `\"MXN\"` |\n\n---\n\n## Datos del calendario (inyectados desde configuración)\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 30 | `calendario_conectado` | `boolean` | Obligatorio | `true` si hay un calendario conectado y operativo. `false` si no. | `true` |\n| 31 | `calendario_tipo` | `string` (enum) | Obligatorio (si `calendario_conectado` = `true`) | Tipo de calendario conectado. Valores: `google_calendar`, `outlook`, `ical`, `sistema_interno`, `otro`. | `\"google_calendar\"` |\n| 32 | `calendario_api_key` | `string` | Opcional (requerido si `calendario_tipo` no es `sistema_interno`) | API key o token de acceso al calendario. | `\"AIzaSy...\"` |\n| 33 | `calendario_id` | `string` | Opcional (requerido si `calendario_tipo` no es `sistema_interno`) | ID del calendario específico en la plataforma. | `\"fitzonepremium@gmail.com\"` |\n| 34 | `dias_calendario_consulta` | `integer` | Obligatorio | Número de días naturales hacia adelante para consultar disponibilidad. Valor por defecto: `7`. | `7` |\n\n---\n\n## Clases y servicios del gimnasio (inyectados desde configuración)\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 35 | `clases_disponibles` | `array` de objects | Obligatorio | Lista de clases y servicios que ofrecen prueba gratuita. (ver estructura) | (ver abajo) |\n\n### Estructura de cada clase en `clases_disponibles`\n\n```json\n{\n  \"clase_id\": \"CLASE-SPINNING\",\n  \"clase_nombre\": \"Spinning\",\n  \"clase_codigo\": \"spinning\",\n  \"duracion_minutos\": 60,\n  \"cupo_maximo\": 25,\n  \"instructores\": [\"Coach Martínez\", \"Coach López\"],\n  \"horarios\": [\n    {\"dia\": \"lunes\", \"hora_inicio\": \"07:00\", \"hora_fin\": \"08:00\", \"instructor\": \"Coach Martínez\"},\n    {\"dia\": \"miércoles\", \"hora_inicio\": \"07:00\", \"hora_fin\": \"08:00\", \"instructor\": \"Coach Martínez\"},\n    {\"dia\": \"viernes\", \"hora_inicio\": \"18:00\", \"hora_fin\": \"19:00\", \"instructor\": \"Coach López\"}\n  ],\n  \"disponible_prueba_gratuita\": true,\n  \"precio_suelto\": 80.00,\n  \"activo\": true\n}\n```\n\n| Campo | Tipo | Obligatorio | Descripción |\n|---|---|---|---|\n| `clase_id` | `string` | Obligatorio | ID único de la clase. Formato: `CLASE-XXXXX`. |\n| `clase_nombre` | `string` | Obligatorio | Nombre legible de la clase. |\n| `clase_codigo` | `string` | Obligatorio | Código de la clase en minúsculas (para matching con el mensaje del usuario). |\n| `duracion_minutos` | `integer` | Obligatorio | Duración de la clase en minutos. |\n| `cupo_maximo` | `integer` | Obligatorio | Cupo máximo de la clase. |\n| `instructores` | `array` de strings | Obligatorio | Lista de instructores que imparten esta clase. |\n| `horarios` | `array` de objects | Obligatorio | Horarios recurrentes de la clase. |\n| `disponible_prueba_gratuita` | `boolean` | Obligatorio | `true` si esta clase admite prueba gratuita. |\n| `precio_suelto` | `number` | Opcional | Precio por clase sin membresía. `null` si no aplica. |\n| `activo` | `boolean` | Obligatorio | `true` si la clase está activa y disponible. |\n\n### Estructura de cada horario en `horarios`\n\n| Campo | Tipo | Descripción |\n|---|---|---|\n| `dia` | `string` | Día de la semana en minúsculas: `lunes`, `martes`, `miércoles`, `jueves`, `viernes`, `sábado`, `domingo`. |\n| `hora_inicio` | `string` | Hora de inicio en formato 24h: `HH:MM`. |\n| `hora_fin` | `string` | Hora de fin en formato 24h: `HH:MM`. |\n| `instructor` | `string` | Nombre del instructor asignado a este horario. |\n\n---\n\n## Políticas de reserva (inyectadas desde configuración)\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 36 | `politica_reserva` | `object` | Obligatorio | Políticas de reserva del gimnasio. (ver estructura) | (ver abajo) |\n\n### Estructura de `politica_reserva`\n\n```json\n{\n  \"anticipacion_minima_horas\": 2,\n  \"anticipacion_maxima_dias\": 14,\n  \"limite_reservas_por_contacto_mes\": 2,\n  \"cancelacion_sin_penalizacion_horas\": 4,\n  \"recordatorio_24h\": true,\n  \"recordatorio_2h\": true,\n  \"texto_politica\": \"Las reservas deben hacerse con al menos 2 horas de anticipación y hasta 14 días antes. Cada contacto puede agendar máximo 2 clases de prueba por mes.\"\n}\n```\n\n| Campo | Tipo | Descripción |\n|---|---|---|\n| `anticipacion_minima_horas` | `integer` | Horas mínimas de anticipación para reservar. |\n| `anticipacion_maxima_dias` | `integer` | Días máximos de anticipación para reservar. |\n| `limite_reservas_por_contacto_mes` | `integer` | Número máximo de reservas de prueba por contacto al mes. |\n| `cancelacion_sin_penalizacion_horas` | `integer` | Horas antes de la cita para cancelar sin penalización. |\n| `recordatorio_24h` | `boolean` | `true` si se envía recordatorio 24 horas antes. |\n| `recordatorio_2h` | `boolean` | `true` si se envía recordatorio 2 horas antes. |\n| `texto_politica` | `string` | Texto literal de la política para incluir en respuestas. |\n\n---\n\n## Datos del destinatario de notificación interna\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 37 | `responsable_nombre` | `string` | Obligatorio | Nombre del responsable que recibe notificaciones internas. | `\"Carlos Mendoza\"` |\n| 38 | `responsable_telefono` | `string` | Opcional | Teléfono del responsable. | `\"+52 55 2222 3333\"` |\n| 39 | `responsable_email` | `string` | Opcional | Email del responsable. | `\"carlos.mendoza@fitzonepremium.com\"` |\n| 40 | `canal_notificacion_equipo` | `string` (enum) | Obligatorio | Canal para notificar al equipo interno. Valores: `whatsapp_interno`, `email_interno`. | `\"whatsapp_interno\"` |\n\n---\n\n## Configuración de tiempos (inyectada desde configuración)\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 41 | `plazo_respuesta_oferta_horas` | `integer` | Obligatorio | Horas que el usuario tiene para seleccionar un horario. Valor por defecto: `24`. | `24` |\n| 42 | `plazo_contacto_escalamiento_horas` | `integer` | Obligatorio | Horas dentro de las cuales un humano debe contactar al usuario tras escalamiento. Valor por defecto: `4`. | `4` |\n| 43 | `hora_preferida_envio` | `string` (formato 24h: `HH:MM`) | Obligatorio | Hora preferida para enviar mensajes de seguimiento. Valor por defecto: `\"10:00\"`. | `\"10:00\"` |\n\n---\n\n## Campos calculados por el agente\n\nEstos campos **no se reciben de entrada**; el agente los genera internamente.\n\n| # | Nombre del campo | Tipo de dato | Generado en | Descripción |\n|---|---|---|---|---|\n| 44 | `solicitud_id` | `string` | Paso 1 | ID único de la solicitud. Formato: `TRB-YYYYMMDD-HHMMSS-XXX`. |\n| 45 | `fecha_hora_recepcion` | `string` (ISO 8601) | Paso 1 | Marca de tiempo de recepción de la solicitud. |\n| 46 | `tipo_cita_clasificado` | `string` (enum) | Paso 2 | Tipo de cita clasificado: `CLASE_PRUEBA_GRUPAL`, `VISITA_GUIADA`, `EVALUACION_FISICA`, `SESION_PERSONAL_PRUEBA`. |\n| 47 | `clase_codigo_extraido` | `string` o `null` | Paso 2 | Código de la clase extraído del mensaje del usuario. |\n| 48 | `fecha_preferida_extraida` | `string` o `null` | Paso 2 | Fecha preferida extraída del mensaje del usuario. |\n| 49 | `hora_preferida_extraida` | `string` o `null` | Paso 2 | Hora preferida extraída del mensaje del usuario. |\n| 50 | `horarios_disponibles` | `array` de objects | Paso 2 | Lista de horarios disponibles encontrados en el calendario. |\n| 51 | `horarios_ofrecidos` | `array` de objects | Paso 3 | Los 3 horarios seleccionados para ofrecer al usuario. |\n| 52 | `fecha_hora_envio_oferta` | `string` (ISO 8601) o `null` | Paso 3 | Marca de tiempo de envío de la oferta de horarios. |\n| 53 | `momento_expiracion_oferta` | `string` (ISO 8601) o `null` | Paso 3 | Momento en que expira la oferta (24h después del envío). |\n| 54 | `seleccion_usuario` | `integer` o `null` | Paso 4 | Número de opción seleccionado por el usuario (1, 2 o 3). |\n| 55 | `reserva_id` | `string` | Paso 4 | ID único de la reserva confirmada. Formato: `TRB-YYYYMMDD-HHMMSS-XXX`. |\n| 56 | `evento_calendario_id` | `string` o `null` | Paso 4 | ID del evento creado en el calendario conectado. |\n| 57 | `fecha_hora_confirmacion` | `string` (ISO 8601) o `null` | Paso 4 | Marca de tiempo de confirmación de la reserva. |\n| 58 | `fecha_hora_envio_confirmacion` | `string` (ISO 8601) o `null` | Paso 5 | Marca de tiempo de envío del mensaje de confirmación. |\n| 59 | `estado_envio_confirmacion` | `string` (enum) | Paso 5 | Estado: `generada`, `enviada`, `error_envio`. |\n| 60 | `contacto_es_nuevo` | `boolean` | Paso 6 | `true` si el contacto no existía previamente en la base de datos. |\n| 61 | `fecha_hora_cierre` | `string` (ISO 8601) | Paso 6 | Marca de tiempo de cierre del caso. |\n| 62 | `resultado` | `string` (enum) | Paso 6 | Resultado: `reserva_confirmada`, `escalamiento`, `oferta_expirada`, `envio_fallido`. |\n| 63 | `duracion_proceso_segundos` | `integer` | Paso 6 | Duración total del proceso en segundos. |\n\n---\n\n## Validaciones de entrada\n\n| Campo | Regla | Acción si falla |\n|---|---|---|\n| `evento_id` | No vacío, formato válido | Terminar flujo sin ejecutar |\n| `contacto_id` | Debe seguir formato `CON-XXXXXXXX-XXX` | Terminar flujo sin ejecutar |\n| `mensaje_solicitud` | No vacío, mínimo 3 caracteres, máximo 2000 | Terminar flujo sin ejecutar |\n| `canal_origen` | Debe ser uno de los 5 valores válidos | Usar `\"whatsapp\"` como valor por defecto |\n| `numero_remitente` | Entre 8 y 15 dígitos si `canal_origen` es `whatsapp` o `sms` | Escalar a humano (C3.1) |\n| `email_remitente` | Si `canal_origen` es `email`, debe ser formato válido | Escalar a humano (C3.1) |\n| `nombre_contacto` | No vacío, mínimo 2 caracteres | Usar `\"Contacto\"` como valor por defecto |\n| `telefono_contacto` | Entre 8 y 15 dígitos | Terminar flujo sin ejecutar |\n| `tipo_cita_solicitada` | Debe ser uno de los 4 valores válidos | Si no se puede determinar, ofrecer las 4 opciones para que el usuario elija |\n| `clase_especifica` | Si `tipo_cita_solicitada` es `CLASE_PRUEBA_GRUPAL`, debe ser código de clase válido | Escalar a humano (C1.2) |\n| `fecha_preferida` | Si se proporciona, formato `YYYY-MM-DD`, no anterior a hoy | Ignorar fecha y ofrecer opciones más cercanas |\n| `hora_preferida` | Si se proporciona, formato `HH:MM` | Ignorar hora y ofrecer horarios dentro del horario de atención |\n| `gimnasio_id` | No vacío | Terminar flujo sin ejecutar |\n| `gimnasio_nombre` | No vacío | Terminar flujo sin ejecutar |\n| `gimnasio_telefono` | No vacío, formato válido | Terminar flujo sin ejecutar |\n| `gimnasio_direccion` | No vacío | Terminar flujo sin ejecutar |\n| `moneda` | Código ISO 4217 válido | Usar `\"MXN\"` como valor por defecto |\n| `calendario_conectado` | Debe ser `true` o `false` | Usar `false` como valor por defecto |\n| `clases_disponibles` | Array con al menos 1 clase activa | Escalar a humano (C2.1) |\n| `politica_reserva` | Objeto con todos los campos requeridos | Usar valores por defecto del sistema |\n| `responsable_nombre` | No vacío | Usar `\"Coordinador de Clases\"` como valor por defecto |\n| `canal_notificacion_equipo` | Debe ser uno de los 2 valores válidos | Usar `\"whatsapp_interno\"` como valor por defecto |\n"
              }
            ]
          }
        ]
      },
      {
        "id": "class-reminder",
        "carpeta": "class-reminder-agent",
        "nombre": "Class Reminder Agent",
        "icono": "🔔",
        "descripcion": "Enviar recordatorios automáticos de clases reservadas antes de la sesión",
        "submodulos": [],
        "categoria": "general",
        "estructura": [
          {
            "id": "class-reminder-agent",
            "etiqueta": "",
            "es_plantilla": true,
            "nombre": "class-reminder-agent",
            "subcarpetas": [],
            "notas": [
              {
                "id": "class-reminder-agent/flujo.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "flujo.md",
                "contenido": "# Flujo de ejecución — Class Reminder Agent\n\nEste archivo documenta el flujo fijo de ejecución del agente. Cada recordatorio sigue estos 6 pasos **en orden**, sin pasos condicionales ni bifurcaciones.\n\n---\n\n## Paso 1 — Consulta de la fecha/hora del evento o cita asociado al contacto\n\nEl agente consulta el sistema del gimnasio para obtener los datos del evento o cita programada que asociada al contacto.\n\n**Acción del agente:**\n1. Recibir el evento disparador `evento_programado`.\n2. Extraer los campos de entrada definidos en `variables-entrada.md`.\n3. Registrar la marca de tiempo de recepción en `fecha_hora_recepcion`.\n4. Generar un `recordatorio_id` único para este ciclo de recordatorio.\n5. Verificar que el `evento_id` tenga un formato válido.\n6. Verificar que el `estado_evento` sea `confirmado` (no cancelado ni completado).\n7. Obtener la `fecha_evento` y `hora_inicio` del evento asociado.\n8. Calcular la `hora_fin` = `hora_inicio` + `duracion_minutos` (según la tabla de duración de `prompt-sistema.md`).\n9. Obtener los datos del contacto: `nombre_contacto`, `telefono_contacto`, `email_contacto`, `canal_preferido`.\n10. Obtener los datos del gimnasio: `gimnasio_nombre`, `gimnasio_direccion`, `gimnasio_telefono`.\n\n**Ejemplo de entrada:**\n\n```json\n{\n  \"evento\": \"evento_programado\",\n  \"evento_id\": \"EVT-20260823-103045-501\",\n  \"contacto_id\": \"CON-20260823-201\",\n  \"nombre_contacto\": \"Ana García López\",\n  \"telefono_contacto\": \"+52 55 9876 5432\",\n  \"email_contacto\": null,\n  \"canal_preferido\": \"whatsapp\",\n  \"tipo_evento\": \"CLASE_GRUPAL\",\n  \"clase_nombre\": \"Spinning\",\n  \"instructor_nombre\": \"Coach Martínez\",\n  \"fecha_evento\": \"2026-08-25\",\n  \"hora_inicio\": \"07:00\",\n  \"duracion_minutos\": 60,\n  \"estado_evento\": \"confirmado\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"gimnasio_direccion\": \"Av. Constituyentes 1230, Col. Las Américas, CDMX\",\n  \"gimnasio_telefono\": \"+52 55 4321 8765\"\n}\n```\n\n---\n\n## Paso 2 — Cálculo del momento de envío según reglas fijas\n\nEl agente calcula el momento exacto en que debe enviar cada recordatorio, aplicando las reglas de tiempo configuradas.\n\n**Acción del agente:**\n1. Determinar los intervalos de recordatorio según el `tipo_evento`:\n   - `CLASE_GRUPAL` → 24 horas antes + 1 hora antes.\n   - `CLASE_PRUEBA_GRUPAL` → 24 horas antes + 1 hora antes.\n   - `EVALUACION_FISICA` → 24 horas antes + 1 hora antes.\n   - `SESION_PERSONAL` → 24 horas antes + 2 horas antes.\n   - `VISITA_GUIADA` → 24 horas antes + 1 hora antes.\n2. Calcular `momento_recordatorio_1`: `fecha_evento` + `hora_inicio` – `intervalo_1_horas`.\n3. Calcular `momento_recordatorio_2`: `fecha_evento` + `hora_inicio` – `intervalo_2_horas`.\n4. Verificar si cada `momento_recordatorio` cae dentro del horario de atención (08:00–22:00):\n   - Si es antes de las 08:00 → ajustar a las 08:00 del mismo día.\n   - Si es después de las 22:00 → ajustar a las 08:00 del siguiente día.\n5. Registrar los momentos calculados en `momento_envio_1` y `momento_envio_2`.\n6. Determinar cuál es el próximo recordatorio a enviar según la fecha/hora actual:\n   - Si `fecha_hora_actual` < `momento_envio_1` → el próximo es recordatorio 1 (24h).\n   - Si `momento_envio_1` ≤ `fecha_hora_actual` < `momento_envio_2` → el próximo es recordatorio 2 (1h/2h).\n   - Si `fecha_hora_actual` ≥ `momento_envio_2` → ambos recordatorios ya debieron enviarse.\n\n**Tabla de intervalos por tipo de evento:**\n\n| Tipo de evento | Intervalo 1 | Intervalo 2 |\n|---|---|---|\n| `CLASE_GRUPAL` | 24 horas antes | 1 hora antes |\n| `CLASE_PRUEBA_GRUPAL` | 24 horas antes | 1 hora antes |\n| `EVALUACION_FISICA` | 24 horas antes | 1 hora antes |\n| `SESION_PERSONAL` | 24 horas antes | 2 horas antes |\n| `VISITA_GUIADA` | 24 horas antes | 1 hora antes |\n\n**Ejemplo de cálculo (evento 25/ago 07:00):**\n\n```json\n{\n  \"evento_id\": \"EVT-20260823-103045-501\",\n  \"tipo_evento\": \"CLASE_GRUPAL\",\n  \"fecha_evento\": \"2026-08-25\",\n  \"hora_inicio\": \"07:00\",\n  \"intervalo_1_horas\": 24,\n  \"intervalo_2_horas\": 1,\n  \"momento_recordatorio_1_calculado\": \"2026-08-24T07:00:00\",\n  \"momento_recordatorio_1_ajustado\": \"2026-08-24T08:00:00\",\n  \"ajuste_recordatorio_1\": true,\n  \"razon_ajuste_1\": \"Momento calculado (07:00) antes del horario de atención (08:00), ajustado a 08:00\",\n  \"momento_recordatorio_2_calculado\": \"2026-08-25T06:00:00\",\n  \"momento_recordatorio_2_ajustado\": \"2026-08-25T08:00:00\",\n  \"ajuste_recordatorio_2\": true,\n  \"razon_ajuste_2\": \"Momento calculado (06:00) antes del horario de atención (08:00), ajustado a 08:00 del mismo día\",\n  \"proximo_recordatorio\": 1\n}\n```\n\n---\n\n## Paso 3 — Generación del mensaje de recordatorio con datos concretos del evento\n\nEl agente genera el texto del mensaje de recordatorio usando la plantilla correspondiente según el número de recordatorio (1 o 2).\n\n**Acción del agente:**\n1. Determinar si es el `recordatorio_1` (24h) o `recordatorio_2` (1h/2h).\n2. Seleccionar la plantilla de `formato-salida.md`:\n   - Recordatorio 1 → sección 1.1 (24 horas antes).\n   - Recordatorio 2 → sección 1.2 (1 hora / 2 horas antes).\n3. Insertar los datos concretos del evento en la plantilla:\n   - `nombre_contacto`: nombre del contacto.\n   - `clase_nombre`: nombre de la clase o tipo de evento.\n   - `dia_semana`: día de la semana de la fecha del evento.\n   - `fecha_clase_formateada`: fecha completa formateada en español.\n   - `hora_inicio` y `hora_fin`: horas del evento.\n   - `instructor_nombre`: nombre del instructor.\n   - `gimnasio_nombre` y `gimnasio_direccion`: datos del gimnasio.\n4. Si es el recordatorio 1, incluir la solicitud de confirmación de asistencia.\n5. Registrar el `texto_recordatorio_generado`.\n6. Registrar `estado_generacion` como `generado`.\n\n**Ejemplo de generación (recordatorio 1 — 24h antes):**\n\n```json\n{\n  \"recordatorio_id\": \"RMA-20260824-080000-001\",\n  \"evento_id\": \"EVT-20260823-103045-501\",\n  \"numero_recordatorio\": 1,\n  \"texto_recordatorio_generado\": \"¡Hola Ana García López! 🏋️ Mañana tienes clase de Spinning en FitZone Premium.\\n\\n📅 Fecha: Lunes 25 de agosto de 2026\\n🕐 Hora: 07:00–08:00\\n👨‍🏫 Instructor: Coach Martínez\\n📍 Lugar: Av. Constituyentes 1230, Col. Las Américas, CDMX\\n\\n¿Confirmas tu asistencia? Responde \\\"✅ Voy\\\" o \\\"❌ No puedo\\\".\\n— FitZone Premium\",\n  \"estado_generacion\": \"generado\"\n}\n```\n\n---\n\n## Paso 4 — Envío del mensaje por el canal configurado\n\nEl agente envía el mensaje de recordatorio al contacto por el canal configurado.\n\n**Acción del agente:**\n1. Determinar el `canal_envio` según `canal_preferido` del contacto. Si `canal_preferido` es `null`, usar `whatsapp` como valor por defecto.\n2. Verificar que el `canal_envio` sea un canal válido (`whatsapp`, `sms`, `email`, `app`).\n3. Adaptar el formato del mensaje según el canal:\n   - `whatsapp` / `sms`: texto con emojis (sin markdown de negritas, que sms no soporta bien).\n   - `email`: asunto + cuerpo con formato.\n   - `app`: notificación push con título + cuerpo corto.\n4. Enviar el mensaje por el `canal_envio`.\n5. Si el envío falla, reintentar una sola vez después de 30 segundos.\n6. Si el segundo intento falla, registrar el error y activar escalamiento (ver `reglas-escalamiento.md` → C3).\n7. Registrar la marca de tiempo de envío en `fecha_hora_envio`.\n8. Actualizar `estado_envio` a `enviada`.\n9. Si es el recordatorio 1, iniciar el contador de plazo de respuesta: **6 horas** desde el envío.\n10. Calcular el `momento_expiracion_respuesta`: `fecha_hora_envio` + 6 horas.\n\n**Tabla de canales y formatos:**\n\n| Canal | Formato de mensaje | Notas |\n|---|---|---|\n| `whatsapp` | Texto con emojis | Formato completo con negritas y emojis |\n| `sms` | Texto plano, máximo 160 caracteres por segmento | Sin markdown. Emojis sí permitidos. |\n| `email` | Asunto + cuerpo separados | Formato completo con negritas y emojis |\n| `app` | Notificación push (título + cuerpo) | Cuerpo máximo 100 caracteres. Enlace al detalle. |\n\n---\n\n## Paso 5 — Registro de la confirmación o falta de respuesta del contacto\n\nEl agente registra la respuesta del contacto o la ausencia de respuesta dentro del plazo definido.\n\n**Acción del agente:**\n1. Esperar la respuesta del contacto dentro del plazo de **6 horas** desde el envío del recordatorio 1 (o hasta la hora del evento si es recordatorio 2).\n2. Clasificar la respuesta recibida (o la falta de ella):\n\n| Respuesta del contacto | Clasificación | Acción |\n|---|---|---|\n| \"Voy\", \"Asistiré\", \"Sí\", \"✅\", \"Confirmado\", \"Allí estaré\" | `confirmado` | Registrar confirmación. No enviar recordatorio 2. Cerrar caso. |\n| \"No puedo\", \"Cancelar\", \"❌\", \"No voy\", \"Quítame\" | `cancelado` | Registrar cancelación. No enviar recordatorio 2. Notificar al equipo interno si clase grupal con cupo limitado. |\n| \"¿Puedo cambiar la hora?\", \"¿Puedo llevar a alguien?\", pregunta no relacionada | `respuesta_no_clara` | Registrar respuesta. Escalar a humano (ver `reglas-escalamiento.md` → C4). |\n| Sin respuesta en 6 horas | `sin_respuesta` | Registrar falta de respuesta. Proceder al paso 6. |\n\n3. Registrar `respuesta_contacto` con la clasificación obtenida.\n4. Registrar `fecha_hora_respuesta` con la marca de tiempo de la respuesta (o `null` si no hubo respuesta).\n5. Registrar `texto_respuesta` con el texto exacto de la respuesta del contacto (o `null`).\n6. Actualizar `estado_recordatorio` según la clasificación.\n\n**Tabla de respuestas del contacto (recordatorio 1):**\n\n| Texto de respuesta | Clasificación |\n|---|---|\n| \"Voy\", \"Sí\", \"✅\", \"Confirmo\", \"Asistiré\", \"Allí estoy\" | `confirmado` |\n| \"No\", \"No puedo\", \"❌\", \"Cancelar\", \"No voy\", \"Quítame\" | `cancelado` |\n| Contiene \"?\" o no coincide con las anteriores | `respuesta_no_clara` |\n| Sin respuesta en 6 horas | `sin_respuesta` |\n\n**Tabla de respuestas del contacto (recordatorio 2):**\n\n| Texto de respuesta | Clasificación |\n|---|---|\n| Cualquier respuesta positiva o neutra | `confirmado` |\n| \"No\", \"No puedo\", \"❌\", \"Cancelar\" | `cancelado` |\n| Sin respuesta | `sin_respuesta` (cerrar caso, no se envía más) |\n\n**Ejemplo de registro de respuesta:**\n\n```json\n{\n  \"recordatorio_id\": \"RMA-20260824-080000-001\",\n  \"evento_id\": \"EVT-20260823-103045-501\",\n  \"numero_recordatorio\": 1,\n  \"respuesta_contacto\": \"confirmado\",\n  \"texto_respuesta\": \"✅ Voy\",\n  \"fecha_hora_respuesta\": \"2026-08-24T09:15:00\",\n  \"duracion_respuesta_minutos\": 75,\n  \"momento_expiracion_respuesta\": \"2026-08-24T14:00:00\"\n}\n```\n\n---\n\n## Paso 6 — Disparo de segundo recordatorio automático si no hay respuesta\n\nSi el contacto no respondió al primer recordatorio dentro del plazo de 6 horas, el agente envía un segundo recordatorio automático.\n\n**Acción del agente:**\n1. Verificar si `respuesta_contacto` es `sin_respuesta` en el paso 5.\n2. Si es `sin_respuesta`, generar el segundo recordatorio usando la plantilla de `formato-salida.md` → sección 1.2 (recordatorio 2 — 1 hora/2 horas antes).\n3. Enviar el segundo recordatorio por el `canal_envio`.\n4. Si el envío falla, reintentar una sola vez después de 30 segundos.\n5. Si el segundo intento falla, registrar el error y activar escalamiento (ver `reglas-escalamiento.md` → C3).\n6. Registrar la marca de tiempo de envío del segundo recordatorio en `fecha_hora_envio_recordatorio_2`.\n7. Actualizar `recordatorio_2_enviado` a `true`.\n8. Esperar la respuesta del contacto hasta la hora de inicio del evento:\n   - Si responde con confirmación → registrar como `confirmado_ultimo_momento`.\n   - Si responde con cancelación → registrar como `cancelado_ultimo_momento`. Notificar al equipo interno.\n   - Si no responde → registrar como `sin_respuesta_final`.\n9. Generar el registro completo del recordatorio con el formato de `formato-salida.md` → sección 4 (registro JSON).\n10. Registrar la marca de tiempo de cierre en `fecha_hora_cierre`.\n11. Calcular `duracion_proceso_segundos` = `fecha_hora_cierre` - `fecha_hora_recepcion`.\n12. Guardar el registro en el sistema de historial de contactos.\n\n**Tabla de resultados posibles:**\n\n| Resultado | Descripción | Próxima acción del sistema |\n|---|---|---|\n| `confirmado` | El contacto confirmó asistencia en el recordatorio 1 | No enviar recordatorio 2. No requiere seguimiento. |\n| `cancelado` | El contacto canceló asistencia en el recordatorio 1 | Liberar cupo si clase grupal. Notificar al equipo interno. |\n| `confirmado_ultimo_momento` | El contacto confirmó en el recordatorio 2 o después | No requiere seguimiento adicional. |\n| `cancelado_ultimo_momento` | El contacto canceló en el recordatorio 2 o después | Liberar cupo si clase grupal. Notificar al equipo interno. Urgente si faltan menos de 2 horas. |\n| `sin_respuesta_final` | El contacto no respondió a ningún recordatorio | Registrar como inasistencia potencial. No enviar más mensajes. |\n| `respuesta_no_clara` | El contacto respondió con una pregunta | Escalar a humano. |\n| `evento_cancelado` | El evento fue cancelado después de programar el recordatorio | No enviar recordatorio. Cerrar caso. |\n| `envio_fallido` | No se pudo entregar el recordatorio | Escalar a humano (C3). Registro de error guardado. |\n"
              },
              {
                "id": "class-reminder-agent/formato-salida.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "formato-salida.md",
                "contenido": "# Formato de salida — Class Reminder Agent\n\nEste archivo define la estructura exacta de los mensajes y registros que el agente debe producir. No se permite desviarse de estas plantillas.\n\n---\n\n## 1. Mensajes de recordatorio al contacto\n\n### 1.1 Recordatorio 1 — 24 horas antes del evento\n\n#### Plantilla (whatsapp/sms)\n\n```\n¡Hola {{nombre_contacto}}! 🏋️ Mañana tienes {{tipo_evento_nombre}} en {{gimnasio_nombre}}.\n\n📅 **Fecha:** {{dia_semana_evento}} {{fecha_evento_formateada}}\n🕐 **Hora:** {{hora_inicio}}–{{hora_fin}}\n🏋️ **Clase:** {{clase_nombre}}\n👨‍🏫 **Instructor:** {{instructor_nombre}}\n📍 **Lugar:** {{gimnasio_direccion}}\n\n¿Confirmas tu asistencia? Responde \"✅ Voy\" o \"❌ No puedo\".\n— {{gimnasio_nombre}}\n```\n\n#### Plantilla (email)\n\n**Asunto:** Mañana tienes {{clase_nombre}} — {{gimnasio_nombre}}\n\n```\nHola {{nombre_contacto}},\n\nMañana tienes {{tipo_evento_nombre}} en {{gimnasio_nombre}}.\n\n📅 **Fecha:** {{dia_semana_evento}} {{fecha_evento_formateada}}\n🕐 **Hora:** {{hora_inicio}}–{{hora_fin}}\n🏋️ **Clase:** {{clase_nombre}}\n👨‍🏫 **Instructor:** {{instructor_nombre}}\n📍 **Dirección:** {{gimnasio_direccion}}\n\nResponde a este correo con \"✅ Voy\" para confirmar tu asistencia o \"❌ No puedo\" si no podrás asistir.\n\n¡Te esperamos! 💪\n— {{gimnasio_nombre}}\n```\n\n#### Plantilla (push)\n\n**Título:** 🏋️ Mañana tienes {{clase_nombre}}\n**Cuerpo:** {{hora_inicio}} con {{instructor_nombre}} en {{gimnasio_nombre}}. Toca para confirmar.\n\n### Ejemplo renderizado (whatsapp — recordatorio 1)\n\n> ¡Hola Ana García López! 🏋️ Mañana tienes Clase grupal en FitZone Premium.\n>\n> 📅 **Fecha:** Lunes 25 de agosto de 2026\n> 🕐 **Hora:** 07:00–08:00\n> 🏋️ **Clase:** Spinning\n> 👨‍🏫 **Instructor:** Coach Martínez\n> 📍 **Lugar:** Av. Constituyentes 1230, Col. Las Américas, CDMX\n>\n> ¿Confirmas tu asistencia? Responde \"✅ Voy\" o \"❌ No puedo\".\n> — FitZone Premium\n\n### Ejemplo renderizado (email — recordatorio 1)\n\n> **Asunto:** Mañana tienes Spinning — FitZone Premium\n>\n> Hola Ana García López,\n>\n> Mañana tienes Clase grupal en FitZone Premium.\n>\n> 📅 **Fecha:** Lunes 25 de agosto de 2026\n> 🕐 **Hora:** 07:00–08:00\n> 🏋️ **Clase:** Spinning\n> 👨‍🏫 **Instructor:** Coach Martínez\n> 📍 **Dirección:** Av. Constituyentes 1230, Col. Las Américas, CDMX\n>\n> Responde a este correo con \"✅ Voy\" para confirmar tu asistencia o \"❌ No puedo\" si no podrás asistir.\n>\n> ¡Te esperamos! 💪\n> — FitZone Premium\n\n---\n\n### 1.2 Recordatorio 2 — 1 hora antes / 2 horas antes del evento\n\n#### Plantilla (whatsapp/sms)\n\n```\n¡Hola {{nombre_contacto}}! ⏰ Tu {{tipo_evento_nombre}} de **{{clase_nombre}}** empieza en {{intervalo_recordatorio_2}} hora(s).\n\n🕐 **Hora:** {{hora_inicio}}–{{hora_fin}}\n👨‍🏫 **Instructor:** {{instructor_nombre}}\n📍 **Lugar:** {{gimnasio_direccion}}\n\n¡Nos vemos pronto! 💪\n— {{gimnasio_nombre}}\n```\n\n#### Plantilla (email)\n\n**Asunto:** Tu {{clase_nombre}} empieza en {{intervalo_recordatorio_2}} hora(s) — {{gimnasio_nombre}}\n\n```\nHola {{nombre_contacto}},\n\nTu {{tipo_evento_nombre}} de {{clase_nombre}} empieza en {{intervalo_recordatorio_2}} hora(s).\n\n🕐 **Hora:** {{hora_inicio}}–{{hora_fin}}\n👨‍🏫 **Instructor:** {{instructor_nombre}}\n📍 **Dirección:** {{gimnasio_direccion}}\n\n¡Te esperamos! 💪\n— {{gimnasio_nombre}}\n```\n\n#### Plantilla (push)\n\n**Título:** ⏰ {{clase_nombre}} empieza en {{intervalo_recordatorio_2}} hora(s)\n**Cuerpo:** {{hora_inicio}} con {{instructor_nombre}}. ¡Nos vemos pronto!\n\n### Ejemplo renderizado (whatsapp — recordatorio 2, 1 hora antes)\n\n> ¡Hola Ana García López! ⏰ Tu Clase grupal de **Spinning** empieza en 1 hora(s).\n>\n> 🕐 **Hora:** 07:00–08:00\n> 👨‍🏫 **Instructor:** Coach Martínez\n> 📍 **Lugar:** Av. Constituyentes 1230, Col. Las Américas, CDMX\n>\n> ¡Nos vemos pronto! 💪\n> — FitZone Premium\n\n### Ejemplo renderizado (whatsapp — recordatorio 2, 2 horas antes, sesión personal)\n\n> ¡Hola Roberto Ruiz Méndez! ⏰ Tu Sesión personal de **Entrenamiento personal** empieza en 2 hora(s).\n>\n> 🕐 **Hora:** 16:00–17:00\n> 👨‍🏫 **Instructor:** Coach Herrera\n> 📍 **Lugar:** Av. Constituyentes 1230, Col. Las Américas, CDMX\n>\n> ¡Nos vemos pronto! 💪\n> — FitZone Premium\n\n---\n\n## 2. Notificación de cancelación al equipo interno\n\n### Plantilla (whatsapp_interno)\n\n```\n❌ CANCELACIÓN DE ASISTENCIA — {{gimnasio_nombre}}\n==========================================\n\n**Contacto:** {{nombre_contacto}}\n**Teléfono:** {{telefono_contacto}}\n**Canal:** {{canal_preferido}}\n\n🏋️ **Clase:** {{clase_nombre}}\n📅 **Fecha:** {{dia_semana_evento}} {{fecha_evento_formateada}}\n🕐 **Hora:** {{hora_inicio}}–{{hora_fin}}\n👨‍🏫 **Instructor:** {{instructor_nombre}}\n👥 **Cupo:** {{cupo_ocupado}}/{{cupo_maximo}}\n\n💬 **Motivo:** {{texto_respuesta}}\n\n🔗 Registro: {{recordatorio_id}}\n```\n\n### Plantilla (email_interno)\n\n**Asunto:** Cancelación — {{nombre_contacto}} — {{clase_nombre}} {{fecha_evento_formateada}} — {{gimnasio_nombre}}\n\n```\nCancelación de asistencia registrada en {{gimnasio_nombre}}.\n\nContacto: {{nombre_contacto}}\nTeléfono: {{telefono_contacto}}\nCanal: {{canal_preferido}}\n\nClase: {{clase_nombre}}\nFecha: {{dia_semana_evento}} {{fecha_evento_formateada}}\nHora: {{hora_inicio}}–{{hora_fin}}\nInstructor: {{instructor_nombre}}\nCupo: {{cupo_ocupado}}/{{cupo_maximo}}\n\nMotivo: {{texto_respuesta}}\n\nRegistro: {{recordatorio_id}}\n```\n\n### Ejemplo renderizado (whatsapp_interno)\n\n> ❌ CANCELACIÓN DE ASISTENCIA — FitZone Premium\n> ==========================================\n>\n> **Contacto:** Laura Hernández\n> **Teléfono:** +52 55 1122 3344\n> **Canal:** whatsapp\n>\n> 🏋️ **Clase:** CrossFit\n> 📅 **Fecha:** Martes 26 de agosto de 2026\n> 🕐 **Hora:** 07:00–08:00\n> 👨‍🏫 **Instructor:** Coach Vega\n> 👥 **Cupo:** 22/30\n>\n> 💬 **Motivo:** No puedo, tengo una reunión de trabajo\n>\n> 🔗 Registro: RMA-20260825-080000-003\n\n---\n\n## 3. Mensaje de escalamiento al contacto\n\n### Plantilla (whatsapp/sms)\n\n```\nEntendido, {{nombre_contacto}}. 💪 Nuestro equipo se pondrá en contacto contigo para resolver tu consulta sobre la clase de **{{clase_nombre}}**.\n\nSi necesitas algo urgente, llámanos al **{{gimnasio_telefono}}**.\n— {{gimnasio_nombre}}\n```\n\n### Plantilla (email)\n\n**Asunto:** Tu consulta — {{gimnasio_nombre}}\n\n```\nHola {{nombre_contacto}},\n\nNuestro equipo se pondrá en contacto contigo para resolver tu consulta sobre la clase de {{clase_nombre}}.\n\nSi necesitas algo urgente, llámanos al **{{gimnasio_telefono}}** o visítanos en **{{gimnasio_direccion}}**.\n\n¡Te esperamos! 💪\n— {{gimnasio_nombre}}\n```\n\n### Ejemplo renderizado (whatsapp)\n\n> Entendido, Laura Hernández. 💪 Nuestro equipo se pondrá en contacto contigo para resolver tu consulta sobre la clase de **CrossFit**.\n>\n> Si necesitas algo urgente, llámanos al **+52 55 4321 8765**.\n> — FitZone Premium\n\n---\n\n## 4. Registro de recordatorio completo (JSON)\n\nSe genera al finalizar el paso 6 del flujo para cada recordatorio procesado.\n\n```json\n{\n  \"registro_id\": \"RMAREG-YYYYMMDD-HHMMSS-XXX\",\n  \"recordatorio_id\": \"RMA-YYYYMMDD-HHMMSS-XXX\",\n  \"evento_id\": \"EVT-YYYYMMDD-HHMMSS-XXX\",\n  \"contacto_id\": \"CON-YYYYMMDD-XXX\",\n  \"gimnasio_id\": \"string\",\n  \"gimnasio_nombre\": \"string\",\n  \"nombre_contacto\": \"string\",\n  \"telefono_contacto\": \"string\",\n  \"email_contacto\": \"string | null\",\n  \"canal_preferido\": \"whatsapp | sms | email | app\",\n  \"es_miembro_actual\": true | false,\n  \"tipo_evento\": \"CLASE_GRUPAL | CLASE_PRUEBA_GRUPAL | EVALUACION_FISICA | SESION_PERSONAL | VISITA_GUIADA\",\n  \"clase_nombre\": \"string | null\",\n  \"instructor_nombre\": \"string\",\n  \"fecha_evento\": \"YYYY-MM-DD\",\n  \"hora_inicio\": \"HH:MM\",\n  \"hora_fin\": \"HH:MM\",\n  \"duracion_minutos\": 0,\n  \"intervalo_recordatorio_1_horas\": 24,\n  \"intervalo_recordatorio_2_horas\": 1 | 2,\n  \"momento_recordatorio_1_calculado\": \"YYYY-MM-DDTHH:MM:SS\",\n  \"momento_recordatorio_1_ajustado\": \"YYYY-MM-DDTHH:MM:SS\",\n  \"ajuste_recordatorio_1\": true | false,\n  \"razon_ajuste_1\": \"string | null\",\n  \"momento_recordatorio_2_calculado\": \"YYYY-MM-DDTHH:MM:SS\",\n  \"momento_recordatorio_2_ajustado\": \"YYYY-MM-DDTHH:MM:SS\",\n  \"ajuste_recordatorio_2\": true | false,\n  \"razon_ajuste_2\": \"string | null\",\n  \"numero_recordatorio\": 1 | 2,\n  \"texto_recordatorio_generado\": \"string\",\n  \"fecha_hora_envio\": \"YYYY-MM-DDTHH:MM:SS | null\",\n  \"estado_envio\": \"enviada | error_envio\",\n  \"momento_expiracion_respuesta\": \"YYYY-MM-DDTHH:MM:SS | null\",\n  \"respuesta_contacto\": \"confirmado | cancelado | respuesta_no_clara | sin_respuesta\",\n  \"texto_respuesta\": \"string | null\",\n  \"fecha_hora_respuesta\": \"YYYY-MM-DDTHH:MM:SS | null\",\n  \"duracion_respuesta_minutos\": 0 | null,\n  \"recordatorio_2_enviado\": true | false,\n  \"fecha_hora_envio_recordatorio_2\": \"YYYY-MM-DDTHH:MM:SS | null\",\n  \"respuesta_contacto_recordatorio_2\": \"confirmado_ultimo_momento | cancelado_ultimo_momento | sin_respuesta_final | null\",\n  \"resultado\": \"confirmado | cancelado | confirmado_ultimo_momento | cancelado_ultimo_momento | sin_respuesta_final | respuesta_no_clara | evento_cancelado | envio_fallido\",\n  \"fue_escalado\": true | false,\n  \"motivo_escalamiento\": \"string | null\",\n  \"fecha_hora_cierre\": \"YYYY-MM-DDTHH:MM:SS\",\n  \"duracion_proceso_segundos\": 0,\n  \"agente\": \"class-reminder-agent\"\n}\n```\n\n### Ejemplo — Recordatorio 1 con confirmación exitosa\n\n```json\n{\n  \"registro_id\": \"RMAREG-20260824-140005-001\",\n  \"recordatorio_id\": \"RMA-20260824-080000-001\",\n  \"evento_id\": \"EVT-20260823-103045-501\",\n  \"contacto_id\": \"CON-20260823-201\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"nombre_contacto\": \"Ana García López\",\n  \"telefono_contacto\": \"+52 55 9876 5432\",\n  \"email_contacto\": null,\n  \"canal_preferido\": \"whatsapp\",\n  \"es_miembro_actual\": false,\n  \"tipo_evento\": \"CLASE_PRUEBA_GRUPAL\",\n  \"clase_nombre\": \"Spinning\",\n  \"instructor_nombre\": \"Coach Martínez\",\n  \"fecha_evento\": \"2026-08-25\",\n  \"hora_inicio\": \"07:00\",\n  \"hora_fin\": \"08:00\",\n  \"duracion_minutos\": 60,\n  \"intervalo_recordatorio_1_horas\": 24,\n  \"intervalo_recordatorio_2_horas\": 1,\n  \"momento_recordatorio_1_calculado\": \"2026-08-24T07:00:00\",\n  \"momento_recordatorio_1_ajustado\": \"2026-08-24T08:00:00\",\n  \"ajuste_recordatorio_1\": true,\n  \"razon_ajuste_1\": \"Momento calculado (07:00) antes del horario de atención (08:00), ajustado a 08:00\",\n  \"momento_recordatorio_2_calculado\": \"2026-08-25T06:00:00\",\n  \"momento_recordatorio_2_ajustado\": \"2026-08-25T08:00:00\",\n  \"ajuste_recordatorio_2\": true,\n  \"razon_ajuste_2\": \"Momento calculado (06:00) antes del horario de atención (08:00), ajustado a 08:00 del mismo día\",\n  \"numero_recordatorio\": 1,\n  \"texto_recordatorio_generado\": \"¡Hola Ana García López! 🏋️ Mañana tienes Clase de prueba grupal en FitZone Premium.\\n\\n📅 Fecha: Lunes 25 de agosto de 2026\\n🕐 Hora: 07:00–08:00\\n🏋️ Clase: Spinning\\n👨‍🏫 Instructor: Coach Martínez\\n📍 Lugar: Av. Constituyentes 1230, Col. Las Américas, CDMX\\n\\n¿Confirmas tu asistencia? Responde \\\"✅ Voy\\\" o \\\"❌ No puedo\\\".\\n— FitZone Premium\",\n  \"fecha_hora_envio\": \"2026-08-24T08:00:02\",\n  \"estado_envio\": \"enviada\",\n  \"momento_expiracion_respuesta\": \"2026-08-24T14:00:02\",\n  \"respuesta_contacto\": \"confirmado\",\n  \"texto_respuesta\": \"✅ Voy\",\n  \"fecha_hora_respuesta\": \"2026-08-24T09:15:00\",\n  \"duracion_respuesta_minutos\": 75,\n  \"recordatorio_2_enviado\": false,\n  \"fecha_hora_envio_recordatorio_2\": null,\n  \"respuesta_contacto_recordatorio_2\": null,\n  \"resultado\": \"confirmado\",\n  \"fue_escalado\": false,\n  \"motivo_escalamiento\": null,\n  \"fecha_hora_cierre\": \"2026-08-24T09:15:03\",\n  \"duracion_proceso_segundos\": 223,\n  \"agente\": \"class-reminder-agent\"\n}\n```\n\n### Ejemplo — Recordatorio 1 sin respuesta → recordatorio 2 enviado\n\n```json\n{\n  \"registro_id\": \"RMAREG-20260825-100005-002\",\n  \"recordatorio_id\": \"RMA-20260824-080000-002\",\n  \"evento_id\": \"EVT-20260823-150000-502\",\n  \"contacto_id\": \"CON-20260115-101\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"nombre_contacto\": \"Roberto Ruiz Méndez\",\n  \"telefono_contacto\": \"+52 55 4567 8901\",\n  \"email_contacto\": \"roberto.ruiz@correo.com\",\n  \"canal_preferido\": \"whatsapp\",\n  \"es_miembro_actual\": true,\n  \"tipo_evento\": \"SESION_PERSONAL\",\n  \"clase_nombre\": \"Entrenamiento personal\",\n  \"instructor_nombre\": \"Coach Herrera\",\n  \"fecha_evento\": \"2026-08-25\",\n  \"hora_inicio\": \"16:00\",\n  \"hora_fin\": \"17:00\",\n  \"duracion_minutos\": 60,\n  \"intervalo_recordatorio_1_horas\": 24,\n  \"intervalo_recordatorio_2_horas\": 2,\n  \"momento_recordatorio_1_calculado\": \"2026-08-24T16:00:00\",\n  \"momento_recordatorio_1_ajustado\": \"2026-08-24T16:00:00\",\n  \"ajuste_recordatorio_1\": false,\n  \"razon_ajuste_1\": null,\n  \"momento_recordatorio_2_calculado\": \"2026-08-25T14:00:00\",\n  \"momento_recordatorio_2_ajustado\": \"2026-08-25T14:00:00\",\n  \"ajuste_recordatorio_2\": false,\n  \"razon_ajuste_2\": null,\n  \"numero_recordatorio\": 1,\n  \"texto_recordatorio_generado\": \"¡Hola Roberto Ruiz Méndez! 🏋️ Mañana tienes Sesión personal en FitZone Premium.\\n\\n📅 Fecha: Lunes 25 de agosto de 2026\\n🕐 Hora: 16:00–17:00\\n🏋️ Clase: Entrenamiento personal\\n👨‍🏫 Instructor: Coach Herrera\\n📍 Lugar: Av. Constituyentes 1230, Col. Las Américas, CDMX\\n\\n¿Confirmas tu asistencia? Responde \\\"✅ Voy\\\" o \\\"❌ No puedo\\\".\\n— FitZone Premium\",\n  \"fecha_hora_envio\": \"2026-08-24T16:00:01\",\n  \"estado_envio\": \"enviada\",\n  \"momento_expiracion_respuesta\": \"2026-08-24T22:00:01\",\n  \"respuesta_contacto\": \"sin_respuesta\",\n  \"texto_respuesta\": null,\n  \"fecha_hora_respuesta\": null,\n  \"duracion_respuesta_minutos\": null,\n  \"recordatorio_2_enviado\": true,\n  \"fecha_hora_envio_recordatorio_2\": \"2026-08-25T14:00:02\",\n  \"respuesta_contacto_recordatorio_2\": \"confirmado_ultimo_momento\",\n  \"resultado\": \"confirmado_ultimo_momento\",\n  \"fue_escalado\": false,\n  \"motivo_escalamiento\": null,\n  \"fecha_hora_cierre\": \"2026-08-25T15:30:00\",\n  \"duracion_proceso_segundos\": 23400,\n  \"agente\": \"class-reminder-agent\"\n}\n```\n\n### Ejemplo — Respuesta con cancelación (clase grupal con cupo limitado)\n\n```json\n{\n  \"registro_id\": \"RMAREG-20260825-100005-003\",\n  \"recordatorio_id\": \"RMA-20260824-080000-003\",\n  \"evento_id\": \"EVT-20260823-180000-503\",\n  \"contacto_id\": \"CON-20260301-305\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"nombre_contacto\": \"Laura Hernández\",\n  \"telefono_contacto\": \"+52 55 1122 3344\",\n  \"email_contacto\": null,\n  \"canal_preferido\": \"whatsapp\",\n  \"es_miembro_actual\": true,\n  \"tipo_evento\": \"CLASE_GRUPAL\",\n  \"clase_nombre\": \"CrossFit\",\n  \"instructor_nombre\": \"Coach Vega\",\n  \"fecha_evento\": \"2026-08-25\",\n  \"hora_inicio\": \"07:00\",\n  \"hora_fin\": \"08:00\",\n  \"duracion_minutos\": 60,\n  \"intervalo_recordatorio_1_horas\": 24,\n  \"intervalo_recordatorio_2_horas\": 1,\n  \"momento_recordatorio_1_calculado\": \"2026-08-24T07:00:00\",\n  \"momento_recordatorio_1_ajustado\": \"2026-08-24T08:00:00\",\n  \"ajuste_recordatorio_1\": true,\n  \"razon_ajuste_1\": \"Momento calculado (07:00) antes del horario de atención (08:00), ajustado a 08:00\",\n  \"momento_recordatorio_2_calculado\": \"2026-08-25T06:00:00\",\n  \"momento_recordatorio_2_ajustado\": \"2026-08-25T08:00:00\",\n  \"ajuste_recordatorio_2\": true,\n  \"razon_ajuste_2\": \"Momento calculado (06:00) antes del horario de atención (08:00), ajustado a 08:00 del mismo día\",\n  \"numero_recordatorio\": 1,\n  \"texto_recordatorio_generado\": \"¡Hola Laura Hernández! 🏋️ Mañana tienes Clase grupal en FitZone Premium.\\n\\n📅 Fecha: Lunes 25 de agosto de 2026\\n🕐 Hora: 07:00–08:00\\n🏋️ Clase: CrossFit\\n👨‍🏫 Instructor: Coach Vega\\n📍 Lugar: Av. Constituyentes 1230, Col. Las Américas, CDMX\\n\\n¿Confirmas tu asistencia? Responde \\\"✅ Voy\\\" o \\\"❌ No puedo\\\".\\n— FitZone Premium\",\n  \"fecha_hora_envio\": \"2026-08-24T08:00:02\",\n  \"estado_envio\": \"enviada\",\n  \"momento_expiracion_respuesta\": \"2026-08-24T14:00:02\",\n  \"respuesta_contacto\": \"cancelado\",\n  \"texto_respuesta\": \"No puedo, tengo una reunión de trabajo\",\n  \"fecha_hora_respuesta\": \"2026-08-24T10:30:00\",\n  \"duracion_respuesta_minutos\": 150,\n  \"recordatorio_2_enviado\": false,\n  \"fecha_hora_envio_recordatorio_2\": null,\n  \"respuesta_contacto_recordatorio_2\": null,\n  \"resultado\": \"cancelado\",\n  \"fue_escalado\": false,\n  \"motivo_escalamiento\": null,\n  \"fecha_hora_cierre\": \"2026-08-24T10:30:03\",\n  \"duracion_proceso_segundos\": 19803,\n  \"agente\": \"class-reminder-agent\"\n}\n```\n\n---\n\n## 5. Registro de escalamiento a humano (JSON)\n\nSe genera cuando se activa una condición de escalamiento (ver `reglas-escalamiento.md`).\n\n```json\n{\n  \"registro_id\": \"RMAREG-YYYYMMDD-HHMMSS-XXX\",\n  \"recordatorio_id\": \"RMA-YYYYMMDD-HHMMSS-XXX\",\n  \"evento\": \"escalamiento\",\n  \"fecha_hora_ejecucion\": \"YYYY-MM-DDTHH:MM:SS\",\n  \"agente\": \"class-reminder-agent\",\n  \"gimnasio_id\": \"string\",\n  \"gimnasio_nombre\": \"string\",\n  \"nombre_contacto\": \"string\",\n  \"telefono_contacto\": \"string\",\n  \"email_contacto\": \"string | null\",\n  \"canal_preferido\": \"whatsapp | sms | email | app\",\n  \"evento_id\": \"string\",\n  \"tipo_evento\": \"string\",\n  \"clase_nombre\": \"string | null\",\n  \"instructor_nombre\": \"string\",\n  \"fecha_evento\": \"YYYY-MM-DD\",\n  \"hora_inicio\": \"HH:MM\",\n  \"resultado\": \"escalamiento\",\n  \"detalles\": \"Motivo: {{motivo_escalamiento}}.\",\n  \"responsable_escalamiento\": \"string nombre del responsable\",\n  \"canal_escalamiento\": \"whatsapp_interno | email_interno\",\n  \"plazo_respuesta_horas\": 0,\n  \"datos_origen\": {\n    \"recordatorio_id\": \"{{recordatorio_id}}\",\n    \"evento_id\": \"{{evento_id}}\",\n    \"nombre_contacto\": \"{{nombre_contacto}}\",\n    \"telefono_contacto\": \"{{telefono_contacto}}\",\n    \"tipo_evento\": \"{{tipo_evento}}\",\n    \"fecha_evento\": \"{{fecha_evento}}\",\n    \"hora_inicio\": \"{{hora_inicio}}\"\n  }\n}\n```\n\n### Ejemplo — Respuesta del contacto es una pregunta (escalamiento)\n\n```json\n{\n  \"registro_id\": \"RMAREG-20260824-120010-004\",\n  \"recordatorio_id\": \"RMA-20260824-080000-004\",\n  \"evento\": \"escalamiento\",\n  \"fecha_hora_ejecucion\": \"2026-08-24T12:00:10\",\n  \"agente\": \"class-reminder-agent\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"nombre_contacto\": \"Ana García López\",\n  \"telefono_contacto\": \"+52 55 9876 5432\",\n  \"email_contacto\": null,\n  \"canal_preferido\": \"whatsapp\",\n  \"evento_id\": \"EVT-20260823-103045-501\",\n  \"tipo_evento\": \"CLASE_PRUEBA_GRUPAL\",\n  \"clase_nombre\": \"Spinning\",\n  \"instructor_nombre\": \"Coach Martínez\",\n  \"fecha_evento\": \"2026-08-25\",\n  \"hora_inicio\": \"07:00\",\n  \"resultado\": \"escalamiento\",\n  \"detalles\": \"Motivo: Respuesta del contacto no clasificable como confirmación o cancelación. Texto: '¿Puedo llevar a mi amiga a la clase?'. Requiere intervención humana.\",\n  \"responsable_escalamiento\": \"Carlos Mendoza\",\n  \"canal_escalamiento\": \"whatsapp_interno\",\n  \"plazo_respuesta_horas\": 4,\n  \"datos_origen\": {\n    \"recordatorio_id\": \"RMA-20260824-080000-004\",\n    \"evento_id\": \"EVT-20260823-103045-501\",\n    \"nombre_contacto\": \"Ana García López\",\n    \"telefono_contacto\": \"+52 55 9876 5432\",\n    \"tipo_evento\": \"CLASE_PRUEBA_GRUPAL\",\n    \"fecha_evento\": \"2026-08-25\",\n    \"hora_inicio\": \"07:00\"\n  }\n}\n```\n\n### Ejemplo — Fallo de envío tras reintentos\n\n```json\n{\n  \"registro_id\": \"RMAREG-20260824-080035-005\",\n  \"recordatorio_id\": \"RMA-20260824-080000-005\",\n  \"evento\": \"escalamiento\",\n  \"fecha_hora_ejecucion\": \"2026-08-24T08:00:35\",\n  \"agente\": \"class-reminder-agent\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"nombre_contacto\": \"María López\",\n  \"telefono_contacto\": \"+52 55 5566 7788\",\n  \"email_contacto\": null,\n  \"canal_preferido\": \"whatsapp\",\n  \"evento_id\": \"EVT-20260823-190000-505\",\n  \"tipo_evento\": \"EVALUACION_FISICA\",\n  \"clase_nombre\": \"Evaluación física\",\n  \"instructor_nombre\": \"Coach Díaz\",\n  \"fecha_evento\": \"2026-08-25\",\n  \"hora_inicio\": \"09:00\",\n  \"resultado\": \"escalamiento\",\n  \"detalles\": \"Motivo: Error de envío por canal whatsapp tras 2 intentos fallidos al enviar recordatorio 1 a María López (evento EVT-20260823-190000-505).\",\n  \"responsable_escalamiento\": \"Carlos Mendoza\",\n  \"canal_escalamiento\": \"whatsapp_interno\",\n  \"plazo_respuesta_horas\": 2,\n  \"datos_origen\": {\n    \"recordatorio_id\": \"RMA-20260824-080000-005\",\n    \"evento_id\": \"EVT-20260823-190000-505\",\n    \"nombre_contacto\": \"María López\",\n    \"telefono_contacto\": \"+52 55 5566 7788\",\n    \"tipo_evento\": \"EVALUACION_FISICA\",\n    \"fecha_evento\": \"2026-08-25\",\n    \"hora_inicio\": \"09:00\"\n  }\n}\n```\n\n---\n\n## 6. Formato de fecha para mensajes al contacto\n\nLas fechas se presentan en el siguiente formato para los mensajes al contacto:\n\n| Elemento | Formato | Ejemplo |\n|---|---|---|\n| `fecha_evento_formateada` | Día + mes + año en español | `\"25 de agosto de 2026\"` |\n| `dia_semana_evento` | Día de la semana en español | `\"Lunes\"` |\n\n### Tabla de meses\n\n| Mes | Formato |\n|---|---|\n| Enero | `\"enero\"` |\n| Febrero | `\"febrero\"` |\n| Marzo | `\"marzo\"` |\n| Abril | `\"abril\"` |\n| Mayo | `\"mayo\"` |\n| Junio | `\"junio\"` |\n| Julio | `\"julio\"` |\n| Agosto | `\"agosto\"` |\n| Septiembre | `\"septiembre\"` |\n| Octubre | `\"octubre\"` |\n| Noviembre | `\"noviembre\"` |\n| Diciembre | `\"diciembre\"` |\n\n### Tabla de días de la semana\n\n| Día | Formato |\n|---|---|\n| Lunes | `\"Lunes\"` |\n| Martes | `\"Martes\"` |\n| Miércoles | `\"Miércoles\"` |\n| Jueves | `\"Jueves\"` |\n| Viernes | `\"Viernes\"` |\n| Sábado | `\"Sábado\"` |\n| Domingo | `\"Domingo\"` |\n\n### Tabla de tipos de evento (nombre legible)\n\n| Tipo de evento | Nombre legible |\n|---|---|\n| `CLASE_GRUPAL` | `\"Clase grupal\"` |\n| `CLASE_PRUEBA_GRUPAL` | `\"Clase de prueba grupal\"` |\n| `EVALUACION_FISICA` | `\"Evaluación física\"` |\n| `SESION_PERSONAL` | `\"Sesión personal\"` |\n| `VISITA_GUIADA` | `\"Visita guiada\"` |\n"
              },
              {
                "id": "class-reminder-agent/prompt-sistema.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "prompt-sistema.md",
                "contenido": "# Prompt del sistema — Class Reminder Agent\n\n## Identidad\nEres **FitBot**, asistente virtual de **GymPro Fitness Center**. Tu función es enviar recordatorios automáticos antes de clases, entrenamientos, evaluaciones físicas y visitas agendadas de forma puntual y motivadora.\n\n## Tono de comunicación\n- **Puntual y confiable**: el recordatorio llega siempre en el momento correcto. No antes, no después.\n- **Motivador pero no insistente**: transmitir ganas de asistir, pero sin presionar. Máximo 2 mensajes por evento.\n- **Claro y concreto**: el mensaje incluye solo la información esencial: fecha, hora, clase, instructor, dirección. Sin rodeos.\n- **Breve**: máximo 3 párrafos por mensaje. El contacto necesita recordar, no leer un ensayo.\n- **Sin generar ansiedad**: no usar frases como \"no olvides\" o \"es muy importante\". Usar \"te esperamos\" y \"nos vemos pronto\".\n\n## Flujo de ejecución\nEjecuta los siguientes pasos en orden exacto, sin omitir ni reordenar:\n\n### Paso 1 — Consulta de la fecha/hora del evento o cita asociado al contacto\nConsulta el sistema del gimnasio para obtener los datos del evento o cita programada asociada al contacto.\n\n### Paso 2 — Cálculo del momento de envío según reglas fijas\nCalcula el momento exacto en que debe enviar cada recordatorio, aplicando las reglas de tiempo configuradas (24h antes, 1h/2h antes).\n\n### Paso 3 — Generación del mensaje de recordatorio con datos concretos del evento\nGenera el texto del mensaje de recordatorio usando la plantilla correspondiente según el número de recordatorio.\n\n### Paso 4 — Envío del mensaje por el canal configurado\nEnvía el mensaje de recordatorio al contacto por el canal configurado.\n\n### Paso 5 — Registro de la confirmación o falta de respuesta del contacto\nRegistra la respuesta del contacto o la ausencia de respuesta dentro del plazo definido.\n\n### Paso 6 — Disparo de segundo recordatorio automático si no hay respuesta\nSi el contacto no respondió al primer recordatorio dentro del plazo, envía un segundo recordatorio automático.\n\n## Ejemplo de ejecución\n\n**Entrada:**\n- nombre_cliente: \"Ana García López\"\n- telefono: \"+52-55-9876-5432\"\n- evento: \"Clase de Spinning el 25 de agosto a las 07:00\"\n\n**Salida generada (paso 3 — recordatorio 24h antes):**\n```\n¡Hola Ana García! 🏋️ Mañana tienes clase de Spinning en FitZone Premium.\n\n📅 Fecha: Lunes 25 de agosto de 2026\n🕐 Hora: 07:00–08:00\n👨‍🏫 Instructor: Coach Martínez\n📍 Lugar: Av. Constituyentes 1230, Col. Las Américas, CDMX\n\n¿Confirmas tu asistencia? Responde \"✅ Voy\" o \"❌ No puedo\".\n```\n\n## Tipos de eventos que generan recordatorio\n\n| ID | Tipo de evento | Duración (min) | Recordatorios configurados |\n|---|---|---|---|\n| `CLASE_GRUPAL` | Clase grupal | 60 | 24h antes + 1h antes |\n| `CLASE_PRUEBA_GRUPAL` | Clase de prueba grupal | 60 | 24h antes + 1h antes |\n| `EVALUACION_FISICA` | Evaluación física | 45 | 24h antes + 1h antes |\n| `SESION_PERSONAL` | Sesión personal | 60 | 24h antes + 2h antes |\n| `VISITA_GUIADA` | Visita guiada | 30 | 24h antes + 1h antes |\n\n## Restricciones\n- No enviar recordatorios fuera del horario de atención (antes de las 08:00 o después de las 22:00).\n- No compartir información de otros miembros. Cada recordatorio es independiente.\n- Máximo 2 mensajes de recordatorio por evento.\n- Si el evento fue cancelado después de programar el recordatorio, no enviar el recordatorio.\n\n## Reglas de escalamiento\n- Si el envío del recordatorio falla 2 veces consecutivas → escalar a humano.\n- Si el contacto responde con una pregunta no relacionada → escalar a humano.\n- Si el evento fue cancelado o modificado después de programar el recordatorio → escalar a humano.\n- Si el contacto tiene múltiples eventos el mismo día y los horarios están fuera de rango → escalar a humano.\n- Tiempo máximo de respuesta: 2 minutos. Si excede, escalar a equipo humano.\n"
              },
              {
                "id": "class-reminder-agent/reglas-escalamiento.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "reglas-escalamiento.md",
                "contenido": "# Reglas de escalamiento — Class Reminder Agent\n\nEste archivo define la lista cerrada y exacta de condiciones bajo las cuales el agente **debe** transferir el caso a un humano (recepcionista, coordinador de clases o gerente). No existe discrecionalidad: si se cumple alguna condición, el escalamiento es **obligatorio**.\n\n---\n\n## Condiciones de escalamiento\n\n### C1 — Datos del contacto o evento inválidos\n\n| # | Condición | Acción |\n|---|---|---|\n| C1.1 | El `evento_id` tiene formato inválido o está vacío | Escalamiento obligatorio. Motivo: `ID de evento inválido o vacío. No se puede generar recordatorio.` No se procesa el recordatorio. |\n| C1.2 | El `contacto_id` tiene formato inválido o está vacío | Escalamiento obligatorio. Motivo: `ID de contacto inválido o vacío asociado al evento {{evento_id}}.` No se procesa el recordatorio. |\n| C1.3 | El `nombre_contacto` está vacío, es `null` o es genérico (ej. \"Contacto\", \"Sin nombre\") | Escalamiento obligatorio. Motivo: `Nombre del contacto no disponible o genérico. No se puede personalizar el recordatorio para el evento {{evento_id}}.` No se procesa el recordatorio. |\n| C1.4 | El `telefono_contacto` tiene menos de 8 dígitos o más de 15 dígitos y `canal_preferido` es `whatsapp` o `sms` | Escalamiento obligatorio. Motivo: `Teléfono del contacto '{{telefono_contacto}}' no cumple con el formato válido (8-15 dígitos). No es posible enviar recordatorio por {{canal_preferido}}.` No se envía recordatorio. |\n| C1.5 | El `canal_preferido` es `email` pero el `email_contacto` es `null` o está vacío | Escalamiento obligatorio. Motivo: `Canal preferido configurado como 'email' pero el contacto no tiene email registrado. No es posible enviar recordatorio.` No se envía recordatorio. |\n| C1.6 | El `email_contacto` está presente pero no tiene formato válido de email | Escalamiento obligatorio. Motivo: `Email del contacto '{{email_contacto}}' no tiene formato válido. No es posible enviar recordatorio por email.` No se envía recordatorio. |\n| C1.7 | El `estado_evento` no es `confirmado` (es `cancelado`, `completado` o `modificado`) | No se procesa recordatorio. **No escala.** Se registra como `evento_cancelado` o `evento_modificado`. No se genera registro de escalamiento. |\n| C1.8 | La `fecha_evento` es anterior a la fecha actual | No se procesa recordatorio. **No escala.** Se descarta el evento. Motivo: `Evento con fecha pasada ({{fecha_evento}}). Fuera del umbral de procesamiento.` No se genera registro. |\n\n---\n\n### C2 — Datos del evento incompletos\n\n| # | Condición | Acción |\n|---|---|---|\n| C2.1 | El `clase_nombre` es `null` o está vacío y `tipo_evento` es `CLASE_GRUPAL` o `CLASE_PRUEBA_GRUPAL` | Escalamiento obligatorio. Motivo: `Nombre de clase no disponible para el evento {{evento_id}} de tipo {{tipo_evento}}. No se puede generar recordatorio personalizado.` No se envía recordatorio. |\n| C2.2 | El `instructor_nombre` es `null` o está vacío | Escalamiento obligatorio. Motivo: `Nombre del instructor no disponible para el evento {{evento_id}}. No se puede incluir en el recordatorio.` No se envía recordatorio hasta resolver. |\n| C2.3 | La `hora_inicio` es `null` o tiene formato inválido | Escalamiento obligatorio. Motivo: `Hora de inicio no disponible o con formato inválido para el evento {{evento_id}}.` No se procesa el recordatorio. |\n| C2.4 | La `duracion_minutos` es `null`, 0 o mayor a 480 | Escalamiento obligatorio. Motivo: `Duración del evento '{{duracion_minutos}}' minutos no válida para el evento {{evento_id}}. Debe ser entre 1 y 480.` No se procesa el recordatorio. |\n\n---\n\n### C3 — Fallo técnico de envío\n\n| # | Condición | Acción |\n|---|---|---|\n| C3.1 | El envío del recordatorio 1 falla **2 veces consecutivas** (intento original + 1 reintento tras 30 segundos) | Escalamiento obligatorio. Motivo: `Error de envío por canal {{canal_preferido}} tras 2 intentos fallidos al enviar recordatorio 1 a {{nombre_contacto}} (evento {{evento_id}}).` Generar registro de escalamiento. Notificar al equipo interno para contacto directo. Plazo: **2 horas**. |\n| C3.2 | El envío del recordatorio 2 falla **2 veces consecutivas** (intento original + 1 reintento tras 30 segundos) | Escalamiento obligatorio. Motivo: `Error de envío por canal {{canal_preferido}} tras 2 intentos fallidos al enviar recordatorio 2 a {{nombre_contacto}} (evento {{evento_id}}).` Generar registro de escalamiento. Notificar al equipo interno para contacto directo. Plazo: **2 horas**. |\n| C3.3 | El `canal_preferido` no es uno de los 4 valores válidos (`whatsapp`, `sms`, `email`, `app`) | Escalamiento obligatorio. Motivo: `Canal preferido '{{canal_preferido}}' no reconocido para el evento {{evento_id}}.` No se procesa el recordatorio. |\n| C3.4 | El destino del envío (número de teléfono, email o token) está vacío o es inválido | Escalamiento obligatorio. Motivo: `Destino de envío inválido o vacío para canal {{canal_preferido}}.` No se envía recordatorio. |\n\n---\n\n### C4 — Respuesta del contacto no procesable\n\n| # | Condición | Acción |\n|---|---|---|\n| C4.1 | El contacto responde con una pregunta o mensaje que no se clasifica como confirmación ni cancelación (contiene \"?\" o no coincide con los textos base) | Escalamiento obligatorio. Motivo: `Respuesta del contacto no clasificable como confirmación o cancelación. Texto: '{{texto_respuesta}}'. Requiere intervención humana.` Generar registro de escalamiento. Notificar al equipo interno. Plazo: **4 horas**. |\n| C4.2 | El contacto indica que tiene un problema médico o lesión que le impide asistir | Escalamiento obligatorio. Motivo: `Contacto indica problema médico o lesión: '{{texto_respuesta}}'. Requiere seguimiento humano para verificar estado de la membresía y ofrecer alternativas.` Generar registro de escalamiento. Notificar al equipo interno. Plazo: **4 horas**. |\n| C4.3 | El contacto solicita modificar la reserva (cambiar fecha, hora, instructor, etc.) | Escalamiento obligatorio. Motivo: `Contacto solicita modificación de la reserva: '{{texto_respuesta}}'. Requiere intervención humana para procesar cambio en calendario.` Generar registro de escalamiento. Notificar al equipo interno. Plazo: **4 horas**. |\n\n---\n\n### C5 — Restricciones de configuración del gimnasio\n\n| # | Condición | Acción |\n|---|---|---|\n| C5.1 | El `gimnasio_telefono` no está configurado o está vacío (se necesita para incluir en el recordatorio) | Escalamiento obligatorio. Motivo: `Teléfono del gimnasio no configurado. No se puede incluir información de contacto en el recordatorio del evento {{evento_id}}.` No se envía recordatorio. |\n| C5.2 | El `gimnasio_direccion` no está configurado o está vacío | Escalamiento obligatorio. Motivo: `Dirección del gimnasio no configurada. No se puede incluir ubicación en el recordatorio del evento {{evento_id}}.` No se envía recordatorio. |\n| C5.3 | El `zona_horaria` no está configurada o no es una zona horaria IANA válida | **No escala.** Usar `America/Mexico_City` como valor por defecto. Registrar advertencia. |\n\n---\n\n### C6 — Cancelación de clase grupal con cupo limitado\n\n| # | Condición | Acción |\n|---|---|---|\n| C6.1 | El contacto cancela asistencia a una clase grupal (`tipo_evento`: `CLASE_GRUPAL` o `CLASE_PRUEBA_GRUPAL`) y `cupo_ocupado` ≥ `cupo_maximo` * 0.8 | Notificar al equipo interno inmediatamente. Motivo: `Cancelación de asistencia a clase grupal con cupo alto ({{cupo_ocupado}}/{{cupo_maximo}}). Contacto {{nombre_contacto}} no asistirá a {{clase_nombre}} el {{fecha_evento}} a las {{hora_inicio}}.` Generar notificación al equipo interno con formato de `formato-salida.md` → sección 2. **No genera registro de escalamiento.** Es notificación interna, no transferencia de caso. |\n\n---\n\n## Resumen de tiempos de escalamiento\n\n| Condición | Tiempo antes de escalar | Prioridad |\n|---|---|---|\n| Evento_id inválido (C1.1) | 0 segundos (inmediato) | Normal |\n| Contacto_id inválido (C1.2) | 0 segundos (inmediato) | Normal |\n| Nombre genérico (C1.3) | 0 segundos (inmediato) | Normal |\n| Teléfono inválido (C1.4) | 0 segundos (inmediato) | Normal |\n| Email faltante (C1.5) | 0 segundos (inmediato) | Normal |\n| Email inválido (C1.6) | 0 segundos (inmediato) | Normal |\n| Clase sin nombre (C2.1) | 0 segundos (inmediato) | Normal |\n| Instructor sin nombre (C2.2) | 0 segundos (inmediato) | Normal |\n| Hora inválida (C2.3) | 0 segundos (inmediato) | Normal |\n| Duración inválida (C2.4) | 0 segundos (inmediato) | Normal |\n| Fallo envío recordatorio 1 (C3.1) | 0 segundos tras 1 reintento fallido | Normal |\n| Fallo envío recordatorio 2 (C3.2) | 0 segundos tras 1 reintento fallido | Normal |\n| Canal no reconocido (C3.3) | 0 segundos (inmediato) | Normal |\n| Destino inválido (C3.4) | 0 segundos (inmediato) | Normal |\n| Respuesta no procesable (C4.1) | 0 segundos (inmediato) | Normal |\n| Problema médico (C4.2) | 0 segundos (inmediato) | Normal |\n| Solicitud de modificación (C4.3) | 0 segundos (inmediato) | Normal |\n| Teléfono gimnasio no configurado (C5.1) | 0 segundos (inmediato) | Normal |\n| Dirección gimnasio no configurada (C5.2) | 0 segundos (inmediato) | Normal |\n| Zona horaria inválida (C5.3) | N/A (usa default) | N/A |\n| Cancelación con cupo alto (C6.1) | 0 segundos (inmediato) | Normal (notificación, no escalamiento) |\n\n---\n\n## Plazos de respuesta del equipo interno\n\n| Tipo de escalamiento | Plazo de respuesta esperado |\n|---|---|\n| Fallo de envío recordatorio (C3.1, C3.2) | 2 horas |\n| Respuesta no procesable del contacto (C4.1) | 4 horas |\n| Problema médico del contacto (C4.2) | 4 horas |\n| Solicitud de modificación (C4.3) | 4 horas |\n| Datos de contacto inválidos (C1.1–C1.6) | 4 horas |\n| Datos del evento incompletos (C2.1–C2.4) | 4 horas |\n| Canal no reconocido (C3.3) | 4 horas |\n| Destino inválido (C3.4) | 4 horas |\n| Gimnasio sin teléfono/dirección (C5.1, C5.2) | 4 horas |\n\n---\n\n## Proceso de escalamiento\n\nCuando se activa cualquiera de las condiciones C1–C5:\n\n1. El agente genera el registro de escalamiento con `resultado: \"escalamiento\"` usando el formato de `formato-salida.md` → sección 5.\n2. El agente envía al contacto el mensaje de escalamiento (solo para C4.1–C4.3) según `formato-salida.md` → sección 3.\n3. El agente notifica al equipo interno con los datos del registro de escalamiento.\n4. El agente actualiza `resultado` a `respuesta_no_clara` o `envio_fallido` según corresponda.\n5. El agente espera la resolución del equipo interno dentro del plazo correspondiente.\n6. Si el equipo interno no responde dentro del plazo, el agente notifica al responsable con nivel de urgencia correspondiente.\n\n**Nota:** Para C6.1 (cancelación con cupo alto), el proceso es diferente: se genera una **notificación** al equipo interno (no un escalamiento) con el formato de `formato-salida.md` → sección 2. No se genera registro de escalamiento ni se espera resolución.\n\n---\n\n## Textos base de escalamiento\n\nLos siguientes textos se usan literalmente en los registros de escalamiento. No se deben modificar ni parafrasear.\n\n| Condición | Texto del campo `detalles` |\n|---|---|\n| C1.1 | `\"Motivo: ID de evento inválido o vacío. No se puede generar recordatorio.\"` |\n| C1.2 | `\"Motivo: ID de contacto inválido o vacío asociado al evento {{evento_id}}.\"` |\n| C1.3 | `\"Motivo: Nombre del contacto no disponible o genérico. No se puede personalizar el recordatorio para el evento {{evento_id}}.\"` |\n| C1.4 | `\"Motivo: Teléfono del contacto '{{telefono_contacto}}' no cumple con el formato válido (8-15 dígitos). No es posible enviar recordatorio por {{canal_preferido}}.\"` |\n| C1.5 | `\"Motivo: Canal preferido configurado como 'email' pero el contacto no tiene email registrado. No es posible enviar recordatorio.\"` |\n| C1.6 | `\"Motivo: Email del contacto '{{email_contacto}}' no tiene formato válido. No es posible enviar recordatorio por email.\"` |\n| C2.1 | `\"Motivo: Nombre de clase no disponible para el evento {{evento_id}} de tipo {{tipo_evento}}. No se puede generar recordatorio personalizado.\"` |\n| C2.2 | `\"Motivo: Nombre del instructor no disponible para el evento {{evento_id}}. No se puede incluir en el recordatorio.\"` |\n| C2.3 | `\"Motivo: Hora de inicio no disponible o con formato inválido para el evento {{evento_id}}.\"` |\n| C2.4 | `\"Motivo: Duración del evento '{{duracion_minutos}}' minutos no válida para el evento {{evento_id}}. Debe ser entre 1 y 480.\"` |\n| C3.1 | `\"Motivo: Error de envío por canal {{canal_preferido}} tras 2 intentos fallidos al enviar recordatorio 1 a {{nombre_contacto}} (evento {{evento_id}}).\"` |\n| C3.2 | `\"Motivo: Error de envío por canal {{canal_preferido}} tras 2 intentos fallidos al enviar recordatorio 2 a {{nombre_contacto}} (evento {{evento_id}}).\"` |\n| C3.3 | `\"Motivo: Canal preferido '{{canal_preferido}}' no reconocido para el evento {{evento_id}}.\"` |\n| C3.4 | `\"Motivo: Destino de envío inválido o vacío para canal {{canal_preferido}}.\"` |\n| C4.1 | `\"Motivo: Respuesta del contacto no clasificable como confirmación o cancelación. Texto: '{{texto_respuesta}}'. Requiere intervención humana.\"` |\n| C4.2 | `\"Motivo: Contacto indica problema médico o lesión: '{{texto_respuesta}}'. Requiere seguimiento humano para verificar estado de la membresía y ofrecer alternativas.\"` |\n| C4.3 | `\"Motivo: Contacto solicita modificación de la reserva: '{{texto_respuesta}}'. Requiere intervención humana para procesar cambio en calendario.\"` |\n| C5.1 | `\"Motivo: Teléfono del gimnasio no configurado. No se puede incluir información de contacto en el recordatorio del evento {{evento_id}}.\"` |\n| C5.2 | `\"Motivo: Dirección del gimnasio no configurada. No se puede incluir ubicación en el recordatorio del evento {{evento_id}}.\"` |\n\n---\n\n## Ejemplo completo de activación\n\n**Condición activada:** C4.1 (respuesta del contacto es una pregunta)\n\n**Contexto:** El contacto Ana García López respondió al recordatorio 1 de su clase de prueba de Spinning con: \"¿Puedo llevar a mi amiga a la clase?\"\n\n**Flujo del agente:**\n1. Recibe respuesta del contacto.\n2. Clasifica como `respuesta_no_clara` (contiene \"?\" y no coincide con confirmación/cancelación).\n3. Activa escalamiento obligatorio (C4.1).\n4. Envía respuesta al contacto con mensaje de escalamiento.\n5. Genera registro de escalamiento.\n6. Notifica al equipo interno.\n\n**Respuesta al contacto:**\n\n> Entendido, Ana García. 💪 Nuestro equipo se pondrá en contacto contigo para resolver tu consulta sobre la clase de **Spinning**.\n>\n> Si necesitas algo urgente, llámanos al **+52 55 4321 8765**.\n> — FitZone Premium\n\n**Registro de escalamiento:**\n\n```json\n{\n  \"registro_id\": \"RMAREG-20260824-120010-004\",\n  \"recordatorio_id\": \"RMA-20260824-080000-004\",\n  \"evento\": \"escalamiento\",\n  \"fecha_hora_ejecucion\": \"2026-08-24T12:00:10\",\n  \"agente\": \"class-reminder-agent\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"nombre_contacto\": \"Ana García López\",\n  \"telefono_contacto\": \"+52 55 9876 5432\",\n  \"email_contacto\": null,\n  \"canal_preferido\": \"whatsapp\",\n  \"evento_id\": \"EVT-20260823-103045-501\",\n  \"tipo_evento\": \"CLASE_PRUEBA_GRUPAL\",\n  \"clase_nombre\": \"Spinning\",\n  \"instructor_nombre\": \"Coach Martínez\",\n  \"fecha_evento\": \"2026-08-25\",\n  \"hora_inicio\": \"07:00\",\n  \"resultado\": \"escalamiento\",\n  \"detalles\": \"Motivo: Respuesta del contacto no clasificable como confirmación o cancelación. Texto: '¿Puedo llevar a mi amiga a la clase?'. Requiere intervención humana.\",\n  \"responsable_escalamiento\": \"Carlos Mendoza\",\n  \"canal_escalamiento\": \"whatsapp_interno\",\n  \"plazo_respuesta_horas\": 4,\n  \"datos_origen\": {\n    \"recordatorio_id\": \"RMA-20260824-080000-004\",\n    \"evento_id\": \"EVT-20260823-103045-501\",\n    \"nombre_contacto\": \"Ana García López\",\n    \"telefono_contacto\": \"+52 55 9876 5432\",\n    \"tipo_evento\": \"CLASE_PRUEBA_GRUPAL\",\n    \"fecha_evento\": \"2026-08-25\",\n    \"hora_inicio\": \"07:00\"\n  }\n}\n```\n\n**Ejemplo de activación C6.1 (cancelación con cupo alto):**\n\nLa usuaria Laura Hernández canceló su asistencia a CrossFit (22/30 cupos). Se genera una notificación al equipo interno:\n\n> ❌ CANCELACIÓN DE ASISTENCIA — FitZone Premium\n> ==========================================\n>\n> **Contacto:** Laura Hernández\n> **Teléfono:** +52 55 1122 3344\n> **Canal:** whatsapp\n>\n> 🏋️ **Clase:** CrossFit\n> 📅 **Fecha:** Lunes 25 de agosto de 2026\n> 🕐 **Hora:** 07:00–08:00\n> 👨‍🏫 **Instructor:** Coach Vega\n> 👥 **Cupo:** 22/30\n>\n> 💬 **Motivo:** No puedo, tengo una reunión de trabajo\n>\n> 🔗 Registro: RMA-20260824-080000-003\n"
              },
              {
                "id": "class-reminder-agent/variables-entrada.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "variables-entrada.md",
                "contenido": "# Variables de entrada — Class Reminder Agent\n\nEste archivo define la lista fija y cerrada de campos de datos que el agente necesita recibir para operar. No se admiten campos adicionales a los listados aquí.\n\n---\n\n## Campos del evento disparador\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 1 | `evento_disparador_id` | `string` | Obligatorio | Identificador único del evento disparador. Formato: `RMD-YYYYMMDD-HHMMSS-XXX`. | `\"RMD-20260824-080000-001\"` |\n| 2 | `evento_id` | `string` | Obligatorio | Identificador del evento o cita asociada. Formato: `EVT-YYYYMMDD-HHMMSS-XXX`. | `\"EVT-20260823-103045-501\"` |\n| 3 | `contacto_id` | `string` | Obligatorio | Identificador del contacto asociado al evento. Formato: `CON-YYYYMMDD-XXX`. | `\"CON-20260823-201\"` |\n| 4 | `fecha_evento` | `string` (ISO 8601: `YYYY-MM-DD`) | Obligatorio | Fecha del evento o cita. No puede ser anterior a la fecha actual. | `\"2026-08-25\"` |\n| 5 | `hora_inicio` | `string` (formato 24h: `HH:MM`) | Obligatorio | Hora de inicio del evento. | `\"07:00\"` |\n| 6 | `duracion_minutos` | `integer` | Obligatorio | Duración del evento en minutos. | `60` |\n| 7 | `tipo_evento` | `string` (enum) | Obligatorio | Tipo de evento. Valores: `CLASE_GRUPAL`, `CLASE_PRUEBA_GRUPAL`, `EVALUACION_FISICA`, `SESION_PERSONAL`, `VISITA_GUIADA`. | `\"CLASE_GRUPAL\"` |\n| 8 | `estado_evento` | `string` (enum) | Obligatorio | Estado del evento. Solo se procesan eventos con estado `confirmado`. Valores: `confirmado`, `cancelado`, `completado`, `modificado`. | `\"confirmado\"` |\n\n---\n\n## Datos del contacto\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 9 | `nombre_contacto` | `string` | Obligatorio | Nombre completo del contacto. | `\"Ana García López\"` |\n| 10 | `telefono_contacto` | `string` | Obligatorio | Número telefónico del contacto. Entre 8 y 15 dígitos. | `\"+52 55 9876 5432\"` |\n| 11 | `email_contacto` | `string` | Opcional (requerido si `canal_preferido` = `email`) | Correo electrónico del contacto. | `\"ana.garcia@correo.com\"` |\n| 12 | `canal_preferido` | `string` (enum) | Obligatorio | Canal preferido para enviar recordatorios. Valores: `whatsapp`, `sms`, `email`, `app`. | `\"whatsapp\"` |\n| 13 | `es_miembro_actual` | `boolean` | Obligatorio | `true` si el contacto es miembro activo del gimnasio. `false` si es prospecto (clase de prueba). | `true` |\n| 14 | `membresia_id` | `string` | Opcional (requerido si `es_miembro_actual` = `true`) | Identificador de la membresía activa. Formato: `MEM-YYYYMMDD-XXX`. | `\"MEM-20260115-101\"` |\n| 15 | `plan_actual` | `string` | Opcional (requerido si `es_miembro_actual` = `true`) | Plan de membresía actual. Valores: `basico`, `premium`, `elite`. | `\"premium\"` |\n\n---\n\n## Datos del evento/clase\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 16 | `clase_nombre` | `string` | Obligatorio (requerido si `tipo_evento` es `CLASE_GRUPAL` o `CLASE_PRUEBA_GRUPAL`) | Nombre de la clase. | `\"Spinning\"` |\n| 17 | `clase_codigo` | `string` | Opcional | Código de la clase. | `\"spinning\"` |\n| 18 | `instructor_nombre` | `string` | Obligatorio | Nombre del instructor o responsable del evento. | `\"Coach Martínez\"` |\n| 19 | `sala_nombre` | `string` | Opcional | Nombre de la sala o espacio donde se realiza el evento. | `\"Sala Cycling\"` |\n| 20 | `cupo_maximo` | `integer` | Opcional | Cupo máximo de la clase (si aplica). | `25` |\n| 21 | `cupo_ocupado` | `integer` | Opcional | Cupo actualmente ocupado. | `18` |\n| 22 | `precioEvento` | `number` | Opcional | Precio del evento (0 si es clase incluida en membresía). | `0.00` |\n| 23 | `moneda` | `string` | Obligatorio | Código de moneda ISO 4217. | `\"MXN\"` |\n\n---\n\n## Datos del gimnasio (inyectados desde configuración)\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 24 | `gimnasio_id` | `string` | Obligatorio | Identificador único del gimnasio. | `\"GYM-001\"` |\n| 25 | `gimnasio_nombre` | `string` | Obligatorio | Nombre comercial del gimnasio. | `\"FitZone Premium\"` |\n| 26 | `gimnasio_telefono` | `string` | Obligatorio | Teléfono principal del gimnasio. | `\"+52 55 4321 8765\"` |\n| 27 | `gimnasio_email` | `string` | Obligatorio | Email general del gimnasio. | `\"info@fitzonepremium.com\"` |\n| 28 | `gimnasio_direccion` | `string` | Obligatorio | Dirección completa del gimnasio. | `\"Av. Constituyentes 1230, Col. Las Américas, CDMX\"` |\n| 29 | `zona_horaria` | `string` | Obligatorio | Zona horaria del gimnasio en formato IANA. | `\"America/Mexico_City\"` |\n| 30 | `horario_atencion_inicio` | `string` (formato 24h: `HH:MM`) | Obligatorio | Hora de inicio del horario de atención. | `\"06:00\"` |\n| 31 | `horario_atencion_fin` | `string` (formato 24h: `HH:MM`) | Obligatorio | Hora de fin del horario de atención. | `\"22:00\"` |\n\n---\n\n## Datos del destinatario de notificación interna\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 32 | `responsable_nombre` | `string` | Obligatorio | Nombre del responsable que recibe notificaciones internas. | `\"Carlos Mendoza\"` |\n| 33 | `responsable_telefono` | `string` | Opcional | Teléfono del responsable. | `\"+52 55 2222 3333\"` |\n| 34 | `responsable_email` | `string` | Opcional | Email del responsable. | `\"carlos.mendoza@fitzonepremium.com\"` |\n| 35 | `canal_notificacion_equipo` | `string` (enum) | Obligatorio | Canal para notificar al equipo interno. Valores: `whatsapp_interno`, `email_interno`. | `\"whatsapp_interno\"` |\n\n---\n\n## Configuración de tiempos (inyectada desde configuración)\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 36 | `intervalo_recordatorio_1_horas` | `integer` | Obligatorio | Horas antes del evento para el primer recordatorio. Valor por defecto: `24`. | `24` |\n| 37 | `intervalo_recordatorio_2_clase_horas` | `integer` | Obligatorio | Horas antes del evento para el segundo recordatorio (clases). Valor por defecto: `1`. | `1` |\n| 38 | `intervalo_recordatorio_2_sesion_horas` | `integer` | Obligatorio | Horas antes del evento para el segundo recordatorio (sesiones personales). Valor por defecto: `2`. | `2` |\n| 39 | `plazo_respuesta_horas` | `integer` | Obligatorio | Horas de espera para respuesta del contacto después del recordatorio 1. Valor por defecto: `6`. | `6` |\n| 40 | `hora_preferida_envio` | `string` (formato 24h: `HH:MM`) | Opcional | Hora preferida para enviar mensajes de seguimiento si el recordatorio cae fuera de horario. Valor por defecto: `\"10:00\"`. | `\"10:00\"` |\n| 41 | `envio_fueradehorario_permitido` | `boolean` | Obligatorio | `true` si se permite enviar recordatorios fuera del horario de atención. `false` si se ajusta al horario. | `false` |\n\n---\n\n## Campos calculados por el agente\n\nEstos campos **no se reciben de entrada**; el agente los genera internamente.\n\n| # | Nombre del campo | Tipo de dato | Generado en | Descripción |\n|---|---|---|---|---|\n| 42 | `recordatorio_id` | `string` | Paso 1 | ID único del recordatorio. Formato: `RMA-YYYYMMDD-HHMMSS-XXX`. |\n| 43 | `fecha_hora_recepcion` | `string` (ISO 8601) | Paso 1 | Marca de tiempo de recepción del evento programado. |\n| 44 | `hora_fin` | `string` (formato 24h: `HH:MM`) | Paso 1 | Hora de fin calculada: `hora_inicio` + `duracion_minutos`. |\n| 45 | `dia_semana_evento` | `string` | Paso 1 | Día de la semana de `fecha_evento`. |\n| 46 | `momento_recordatorio_1_calculado` | `string` (ISO 8601) | Paso 2 | Primer momento de envío calculado (sin ajuste de horario). |\n| 47 | `momento_recordatorio_1_ajustado` | `string` (ISO 8601) | Paso 2 | Primer momento de envío ajustado al horario de atención. |\n| 48 | `ajuste_recordatorio_1` | `boolean` | Paso 2 | `true` si se ajustó el momento del recordatorio 1. |\n| 49 | `razon_ajuste_1` | `string` o `null` | Paso 2 | Razón del ajuste del recordatorio 1. |\n| 50 | `momento_recordatorio_2_calculado` | `string` (ISO 8601) | Paso 2 | Segundo momento de envío calculado (sin ajuste de horario). |\n| 51 | `momento_recordatorio_2_ajustado` | `string` (ISO 8601) | Paso 2 | Segundo momento de envío ajustado al horario de atención. |\n| 52 | `ajuste_recordatorio_2` | `boolean` | Paso 2 | `true` si se ajustó el momento del recordatorio 2. |\n| 53 | `razon_ajuste_2` | `string` o `null` | Paso 2 | Razón del ajuste del recordatorio 2. |\n| 54 | `proximo_recordatorio` | `integer` | Paso 2 | Número del próximo recordatorio a enviar (1 o 2). |\n| 55 | `numero_recordatorio` | `integer` | Paso 3 | Número del recordatorio generado (1 o 2). |\n| 56 | `texto_recordatorio_generado` | `string` | Paso 3 | Texto completo del mensaje de recordatorio. |\n| 57 | `estado_generacion` | `string` (enum) | Paso 3 | Estado: `generado`, `error_generacion`. |\n| 58 | `canal_envio` | `string` (enum) | Paso 4 | Canal utilizado para el envío. |\n| 59 | `fecha_hora_envio` | `string` (ISO 8601) o `null` | Paso 4 | Marca de tiempo de envío del recordatorio. |\n| 60 | `estado_envio` | `string` (enum) | Paso 4 | Estado: `enviada`, `error_envio`. |\n| 61 | `momento_expiracion_respuesta` | `string` (ISO 8601) o `null` | Paso 4 | Momento en que expira el plazo de respuesta. |\n| 62 | `respuesta_contacto` | `string` (enum) | Paso 5 | Clasificación: `confirmado`, `cancelado`, `respuesta_no_clara`, `sin_respuesta`. |\n| 63 | `texto_respuesta` | `string` o `null` | Paso 5 | Texto exacto de la respuesta del contacto. |\n| 64 | `fecha_hora_respuesta` | `string` (ISO 8601) o `null` | Paso 5 | Marca de tiempo de la respuesta. |\n| 65 | `duracion_respuesta_minutos` | `integer` o `null` | Paso 5 | Minutos transcurridos entre el envío y la respuesta. |\n| 66 | `recordatorio_2_enviado` | `boolean` | Paso 6 | `true` si se envió el segundo recordatorio. |\n| 67 | `fecha_hora_envio_recordatorio_2` | `string` (ISO 8601) o `null` | Paso 6 | Marca de tiempo de envío del segundo recordatorio. |\n| 68 | `respuesta_contacto_recordatorio_2` | `string` (enum) o `null` | Paso 6 | Respuesta al segundo recordatorio: `confirmado_ultimo_momento`, `cancelado_ultimo_momento`, `sin_respuesta_final`. |\n| 69 | `fecha_hora_cierre` | `string` (ISO 8601) | Paso 6 | Marca de tiempo de cierre del caso. |\n| 70 | `resultado` | `string` (enum) | Paso 6 | Resultado final. |\n| 71 | `duracion_proceso_segundos` | `integer` | Paso 6 | Duración total del proceso en segundos. |\n\n---\n\n## Validaciones de entrada\n\n| Campo | Regla | Acción si falla |\n|---|---|---|\n| `evento_disparador_id` | No vacío, formato válido | Terminar flujo sin ejecutar |\n| `evento_id` | No vacío, formato válido | Terminar flujo sin ejecutar |\n| `contacto_id` | Debe seguir formato `CON-XXXXXXXX-XXX` | Terminar flujo sin ejecutar |\n| `fecha_evento` | Formato `YYYY-MM-DD`, no anterior a hoy | No procesar recordatorio (evento pasado) |\n| `hora_inicio` | Formato `HH:MM` | Terminar flujo sin ejecutar |\n| `duracion_minutos` | Número entero > 0, máximo 480 | Usar `60` como valor por defecto |\n| `tipo_evento` | Debe ser uno de los 5 valores válidos | Terminar flujo sin ejecutar |\n| `estado_evento` | Debe ser `confirmado` | No procesar recordatorio si no es confirmado |\n| `nombre_contacto` | No vacío, mínimo 2 caracteres | Usar `\"Contacto\"` como valor por defecto |\n| `telefono_contacto` | Entre 8 y 15 dígitos si `canal_preferido` es `whatsapp` o `sms` | Escalar a humano (C3.1) |\n| `email_contacto` | Si `canal_preferido` es `email`, debe ser formato válido | Escalar a humano (C3.1) |\n| `canal_preferido` | Debe ser uno de los 4 valores válidos | Usar `\"whatsapp\"` como valor por defecto |\n| `clase_nombre` | Si `tipo_evento` es `CLASE_GRUPAL` o `CLASE_PRUEBA_GRUPAL`, no puede estar vacío | Terminar flujo sin ejecutar |\n| `instructor_nombre` | No vacío | Usar `\"Instructor\"` como valor por defecto |\n| `gimnasio_id` | No vacío | Terminar flujo sin ejecutar |\n| `gimnasio_nombre` | No vacío | Terminar flujo sin ejecutar |\n| `gimnasio_telefono` | No vacío, formato válido | Terminar flujo sin ejecutar |\n| `gimnasio_direccion` | No vacío | Terminar flujo sin ejecutar |\n| `moneda` | Código ISO 4217 válido | Usar `\"MXN\"` como valor por defecto |\n| `responsable_nombre` | No vacío | Usar `\"Coordinador de Clases\"` como valor por defecto |\n| `canal_notificacion_equipo` | Debe ser uno de los 2 valores válidos | Usar `\"whatsapp_interno\"` como valor por defecto |\n"
              }
            ]
          }
        ]
      },
      {
        "id": "membership-renewal",
        "carpeta": "membership-renewal-reminder",
        "nombre": "Membership Renewal Reminder",
        "icono": "🔄",
        "descripcion": "Enviar recordatorios de renovación de membresía antes del vencimiento",
        "submodulos": [],
        "categoria": "general",
        "estructura": [
          {
            "id": "membership-renewal-reminder",
            "etiqueta": "",
            "es_plantilla": true,
            "nombre": "membership-renewal-reminder",
            "subcarpetas": [],
            "notas": [
              {
                "id": "membership-renewal-reminder/flujo.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "flujo.md",
                "contenido": "# Flujo de ejecución — Membership Renewal Reminder\n\nEste archivo documenta el flujo fijo de ejecución del agente. Cada recordatorio de renovación sigue estos 6 pasos **en orden**, sin pasos condicionales ni bifurcaciones.\n\n---\n\n## Paso 1 — Consulta de la fecha/hora del evento o cita asociado al contacto\n\nEl agente consulta el sistema del gimnasio para obtener los datos de la membresía del contacto que está próxima a vencer.\n\n**Acción del agente:**\n1. Recibir el evento disparador `membresia_por_vencer`.\n2. Extraer los campos de entrada definidos en `variables-entrada.md`.\n3. Registrar la marca de tiempo de recepción en `fecha_hora_recepcion`.\n4. Generar un `recordatorio_id` único para este ciclo de recordatorio.\n5. Verificar que el `membresia_id` tenga un formato válido.\n6. Verificar que el `estado_membresia` sea `activa` (no cancelada ni vencida).\n7. Obtener la `fecha_vencimiento` de la membresía.\n8. Calcular los `dias_restantes` = `fecha_vencimiento` – `fecha_actual`.\n9. Obtener los datos del contacto: `nombre_contacto`, `telefono_contacto`, `email_contacto`, `canal_preferido`.\n10. Obtener los datos del plan: `plan_actual_codigo`, `plan_actual_nombre`, `precio_plan`, `beneficios_plan`.\n11. Obtener los datos del gimnasio: `gimnasio_nombre`, `gimnasio_direccion`, `gimnasio_telefono`.\n12. Obtener las políticas de renovación: `descuento_renovacion_porcentaje`, `fecha_limite_descuento`, `renovacion_automatica`.\n\n**Ejemplo de entrada:**\n\n```json\n{\n  \"evento\": \"membresia_por_vencer\",\n  \"membresia_id\": \"MEM-20260115-101\",\n  \"contacto_id\": \"CON-20260115-101\",\n  \"nombre_contacto\": \"Ana García López\",\n  \"telefono_contacto\": \"+52 55 9876 5432\",\n  \"email_contacto\": \"ana.garcia@correo.com\",\n  \"canal_preferido\": \"whatsapp\",\n  \"estado_membresia\": \"activa\",\n  \"plan_actual_codigo\": \"premium\",\n  \"plan_actual_nombre\": \"Premium\",\n  \"precio_plan\": 699.00,\n  \"moneda\": \"MXN\",\n  \"fecha_inicio_membresia\": \"2026-01-15\",\n  \"fecha_vencimiento\": \"2026-09-15\",\n  \"renovacion_automatica\": true,\n  \"descuento_renovacion_porcentaje\": 5,\n  \"fecha_limite_descuento\": \"2026-08-25\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"gimnasio_telefono\": \"+52 55 4321 8765\",\n  \"gimnasio_direccion\": \"Av. Constituyentes 1230, Col. Las Américas, CDMX\"\n}\n```\n\n---\n\n## Paso 2 — Cálculo del momento de envío según reglas fijas\n\nEl agente calcula el momento exacto en que debe enviar cada recordatorio, aplicando las reglas de tiempo configuradas.\n\n**Acción del agente:**\n1. Determinar los intervalos de recordatorio según la configuración:\n   - `intervalo_renovacion_1_dias`: 30 días antes del vencimiento.\n   - `intervalo_renovacion_2_dias`: 15 días antes del vencimiento.\n   - `intervalo_renovacion_3_dias`: 7 días antes del vencimiento.\n   - `intervalo_renovacion_4_dias`: 3 días antes del vencimiento.\n2. Calcular `momento_recordatorio_1`: `fecha_vencimiento` – `intervalo_renovacion_1_dias`.\n3. Calcular `momento_recordatorio_2`: `fecha_vencimiento` – `intervalo_renovacion_2_dias`.\n4. Calcular `momento_recordatorio_3`: `fecha_vencimiento` – `intervalo_renovacion_3_dias`.\n5. Calcular `momento_recordatorio_4`: `fecha_vencimiento` – `intervalo_renovacion_4_dias`.\n6. Verificar si cada `momento_recordatorio` cae dentro del horario de atención (08:00–22:00):\n   - Si es antes de las 08:00 → ajustar a las 10:00 del mismo día.\n   - Si es después de las 22:00 → ajustar a las 10:00 del siguiente día hábil.\n7. Registrar los momentos calculados y ajustados.\n8. Determinar cuál es el próximo recordatorio a enviar según la `fecha_actual`:\n   - Si `fecha_actual` ≤ `momento_recordatorio_1` → el próximo es recordatorio 1 (30 días).\n   - Si `momento_recordatorio_1` < `fecha_actual` ≤ `momento_recordatorio_2` → el próximo es recordatorio 2 (15 días).\n   - Si `momento_recordatorio_2` < `fecha_actual` ≤ `momento_recordatorio_3` → el próximo es recordatorio 3 (7 días).\n   - Si `momento_recordatorio_3` < `fecha_actual` ≤ `momento_recordatorio_4` → el próximo es recordatorio 4 (3 días).\n   - Si `fecha_actual` > `momento_recordatorio_4` → todos los recordatorios ya debieron enviarse.\n\n**Tabla de intervalos por defecto:**\n\n| Recordatorio | Intervalo | Días antes del vencimiento | Ejemplo (vence 15/sep) |\n|---|---|---|---|\n| 1 | 30 días | 30 | 16 de agosto |\n| 2 | 15 días | 15 | 31 de agosto |\n| 3 | 7 días | 7 | 8 de septiembre |\n| 4 | 3 días | 3 | 12 de septiembre |\n\n**Ejemplo de cálculo (membresía vence 15/sep/2026):**\n\n```json\n{\n  \"membresia_id\": \"MEM-20260115-101\",\n  \"fecha_vencimiento\": \"2026-09-15\",\n  \"fecha_actual\": \"2026-08-16\",\n  \"dias_restantes\": 30,\n  \"momento_recordatorio_1_calculado\": \"2026-08-16\",\n  \"momento_recordatorio_1_ajustado\": \"2026-08-16T10:00:00\",\n  \"ajuste_recordatorio_1\": true,\n  \"razon_ajuste_1\": \"Fecha calculada sin hora específica, ajustado a 10:00\",\n  \"momento_recordatorio_2_calculado\": \"2026-08-31\",\n  \"momento_recordatorio_2_ajustado\": \"2026-08-31T10:00:00\",\n  \"ajuste_recordatorio_2\": true,\n  \"razon_ajuste_2\": \"Fecha calculada sin hora específica, ajustado a 10:00\",\n  \"momento_recordatorio_3_calculado\": \"2026-09-08\",\n  \"momento_recordatorio_3_ajustado\": \"2026-09-08T10:00:00\",\n  \"ajuste_recordatorio_3\": true,\n  \"razon_ajuste_3\": \"Fecha calculada sin hora específica, ajustado a 10:00\",\n  \"momento_recordatorio_4_calculado\": \"2026-09-12\",\n  \"momento_recordatorio_4_ajustado\": \"2026-09-12T10:00:00\",\n  \"ajuste_recordatorio_4\": true,\n  \"razon_ajuste_4\": \"Fecha calculada sin hora específica, ajustado a 10:00\",\n  \"proximo_recordatorio\": 1\n}\n```\n\n---\n\n## Paso 3 — Generación del mensaje de recordatorio con datos concretos del evento\n\nEl agente genera el texto del mensaje de recordatorio usando la plantilla correspondiente según el número de recordatorio.\n\n**Acción del agente:**\n1. Determinar el `numero_recordatorio` según el paso 2.\n2. Seleccionar la plantilla de `formato-salida.md`:\n   - Recordatorio 1 (30 días) → sección 1.1.\n   - Recordatorio 2 (15 días) → sección 1.2.\n   - Recordatorio 3 (7 días) → sección 1.3.\n   - Recordatorio 4 (3 días) → sección 1.4.\n3. Insertar los datos concretos de la membresía en la plantilla:\n   - `nombre_contacto`: nombre del miembro.\n   - `plan_actual_nombre`: nombre del plan actual.\n   - `precio_plan`: precio mensual del plan.\n   - `moneda`: código de moneda.\n   - `fecha_vencimiento_formateada`: fecha de vencimiento formateada en español.\n   - `beneficios_resumen`: resumen de beneficios del plan.\n   - `descuento_renovacion_porcentaje`: porcentaje de descuento por renovación anticipada.\n   - `fecha_limite_descuento_formateada`: fecha límite para obtener el descuento.\n   - `gimnasio_nombre`: nombre del gimnasio.\n4. Registrar el `texto_recordatorio_generado`.\n5. Registrar `estado_generacion` como `generado`.\n\n**Ejemplo de generación (recordatorio 1 — 30 días antes):**\n\n```json\n{\n  \"recordatorio_id\": \"MRR-20260816-100000-001\",\n  \"membresia_id\": \"MEM-20260115-101\",\n  \"numero_recordatorio\": 1,\n  \"intervalo_dias\": 30,\n  \"texto_recordatorio_generado\": \"¡Hola Ana García López! 💪 Tu membresía de Premium en FitZone Premium vence el 15 de septiembre de 2026.\\n\\n📋 Plan actual: Premium — $699 MXN/mes\\n✅ Beneficios incluidos: Acceso a gimnasio, cardio, clases grupales ilimitadas, sauna y vapor\\n\\nRenueva antes del 25 de agosto de 2026 y obtén un 5% de descuento.\\n\\n¿Deseas renovar? Responde \\\"🔄 Renovar\\\" o \\\"📋 Ver opciones\\\".\\n— FitZone Premium\",\n  \"estado_generacion\": \"generado\"\n}\n```\n\n---\n\n## Paso 4 — Envío del mensaje por el canal configurado\n\nEl agente envía el mensaje de recordatorio al contacto por el canal configurado.\n\n**Acción del agente:**\n1. Determinar el `canal_envio` según `canal_preferido` del contacto. Si `canal_preferido` es `null`, usar `whatsapp` como valor por defecto.\n2. Verificar que el `canal_envio` sea un canal válido (`whatsapp`, `sms`, `email`, `app`).\n3. Adaptar el formato del mensaje según el canal:\n   - `whatsapp` / `sms`: texto con emojis.\n   - `email`: asunto + cuerpo con formato.\n   - `app`: notificación push con título + cuerpo corto.\n4. Enviar el mensaje por el `canal_envio`.\n5. Si el envío falla, reintentar una sola vez después de 30 segundos.\n6. Si el segundo intento falla, registrar el error y activar escalamiento (ver `reglas-escalamiento.md` → C3).\n7. Registrar la marca de tiempo de envío en `fecha_hora_envio`.\n8. Actualizar `estado_envio` a `enviada`.\n9. Iniciar el contador de plazo de respuesta: **72 horas** (3 días) desde el envío.\n10. Calcular el `momento_expiracion_respuesta`: `fecha_hora_envio` + 72 horas.\n\n**Tabla de canales y formatos:**\n\n| Canal | Formato de mensaje | Notas |\n|---|---|---|\n| `whatsapp` | Texto con emojis y negritas | Formato completo |\n| `sms` | Texto plano, máximo 160 caracteres por segmento | Sin markdown |\n| `email` | Asunto + cuerpo separados | Formato completo con negritas |\n| `app` | Notificación push (título + cuerpo) | Cuerpo máximo 100 caracteres |\n\n---\n\n## Paso 5 — Registro de la confirmación o falta de respuesta del contacto\n\nEl agente registra la respuesta del contacto o la ausencia de respuesta dentro del plazo definido.\n\n**Acción del agente:**\n1. Esperar la respuesta del contacto dentro del plazo de **72 horas** desde el envío del recordatorio.\n2. Clasificar la respuesta recibida (o la falta de ella):\n\n| Respuesta del contacto | Clasificación | Acción |\n|---|---|---|\n| \"Renovar\", \"Sí\", \"🔄 Renovar\", \"Renuevo\", \"Confirmo\" | `renovado` | Registrar renovación. No enviar más recordatorios. Notificar al equipo interno si se requiere procesamiento manual. |\n| \"No renovar\", \"Cancelar\", \"❌\", \"Ya no voy\", \"No quiero\" | `no_renovara` | Registrar decisión. No enviar más recordatorios. Notificar al equipo interno para gestión de baja. |\n| \"Quiero otro plan\", \"¿Puedo bajar?\", \"Cambiar plan\" | `solicitud_cambio` | Registrar solicitud. Escalar a humano (ver `reglas-escalamiento.md` → C4). |\n| \"¿Cuánto falta?\", \"¿Qué beneficios tengo?\", pregunta no relacionada | `respuesta_no_clara` | Registrar respuesta. Escalar a humano (ver `reglas-escalamiento.md` → C4). |\n| Sin respuesta en 72 horas | `sin_respuesta` | Registrar falta de respuesta. Proceder al paso 6. |\n\n3. Registrar `respuesta_contacto` con la clasificación obtenida.\n4. Registrar `fecha_hora_respuesta` con la marca de tiempo de la respuesta (o `null` si no hubo respuesta).\n5. Registrar `texto_respuesta` con el texto exacto de la respuesta del contacto (o `null`).\n6. Actualizar `estado_recordatorio` según la clasificación.\n\n---\n\n## Paso 6 — Disparo de segundo recordatorio automático si no hay respuesta\n\nSi el contacto no respondió al primer recordatorio dentro del plazo de 72 horas, el agente envía un segundo recordatorio automático.\n\n**Acción del agente:**\n1. Verificar si `respuesta_contacto` es `sin_respuesta` en el paso 5.\n2. Si es `sin_respuesta`, generar el segundo recordatorio usando la plantilla de `formato-salida.md` → sección 2 (recordatorio de seguimiento, menos detallado, más directo).\n3. Enviar el segundo recordatorio por el `canal_envio`.\n4. Si el envío falla, reintentar una sola vez después de 30 segundos.\n5. Si el segundo intento falla, registrar el error y activar escalamiento (ver `reglas-escalamiento.md` → C3).\n6. Registrar la marca de tiempo de envío del segundo recordatorio en `fecha_hora_envio_recordatorio_2`.\n7. Actualizar `recordatorio_2_enviado` a `true`.\n8. Esperar la respuesta del contacto hasta la fecha de vencimiento de la membresía:\n   - Si responde con renovación → registrar como `renovado_posterior`.\n   - Si responde con no renovación → registrar como `no_renovara_posterior`. Notificar al equipo interno.\n   - Si no responde → registrar como `sin_respuesta_final`.\n9. Generar el registro completo del recordatorio con el formato de `formato-salida.md` → sección 4 (registro JSON).\n10. Registrar la marca de tiempo de cierre en `fecha_hora_cierre`.\n11. Calcular `duracion_proceso_segundos` = `fecha_hora_cierre` - `fecha_hora_recepcion`.\n12. Guardar el registro en el sistema de historial de contactos.\n\n**Tabla de resultados posibles:**\n\n| Resultado | Descripción | Próxima acción del sistema |\n|---|---|---|\n| `renovado` | El miembro confirmó renovación | Procesar renovación. No enviar más recordatorios. |\n| `no_renovara` | El miembro indicó que no renovará | Gestionar baja. Liberar beneficios. |\n| `solicitud_cambio` | El miembro quiere cambiar de plan | Escalar a humano para asesoría. |\n| `renovado_posterior` | El miembro renovó después del segundo recordatorio | Procesar renovación. |\n| `no_renovara_posterior` | El miembro decidió no renovar después del segundo recordatorio | Gestionar baja. |\n| `sin_respuesta_final` | El miembro no respondió a ningún recordatorio | Registrar como posible baja. Notificar al equipo interno. |\n| `respuesta_no_clara` | El miembro respondió con una pregunta | Escalar a humano. |\n| `ya_renovada` | La membresía ya fue renovada antes del recordatorio | No enviar recordatorio. Cerrar caso. |\n| `envio_fallido` | No se pudo entregar el recordatorio | Escalar a humano (C3). Registro de error guardado. |\n"
              },
              {
                "id": "membership-renewal-reminder/formato-salida.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "formato-salida.md",
                "contenido": "# Formato de salida — Membership Renewal Reminder\n\nEste archivo define la estructura exacta de los mensajes y registros que el agente debe producir. No se permite desviarse de estas plantillas.\n\n---\n\n## 1. Mensajes de recordatorio al miembro — Por intervalo de renovación\n\n### 1.1 Recordatorio 1 — 30 días antes del vencimiento\n\n#### Plantilla (whatsapp/sms)\n\n```\n¡Hola {{nombre_contacto}}! 💪 Tu membresía de **{{plan_actual_nombre}}** en {{gimnasio_nombre}} vence el **{{fecha_vencimiento_formateada}}**.\n\n📋 **Plan actual:** {{plan_actual_nombre}} — ${{precio_plan}} {{moneda}}/mes\n✅ **Beneficios incluidos:** {{beneficios_resumen}}\n\nRenueva antes del **{{fecha_limite_descuento_formateada}}** y obtén un **{{descuento_renovacion_porcentaje}}% de descuento**.\n\n{{texto_renovacion_automatica}}\n\n¿Deseas renovar? Responde \"🔄 Renovar\" o \"📋 Ver opciones\".\n— {{gimnasio_nombre}}\n```\n\n#### Plantilla (email)\n\n**Asunto:** Tu membresía vence el {{fecha_vencimiento_formateada}} — {{gimnasio_nombre}}\n\n```\nHola {{nombre_contacto}},\n\nTu membresía de {{plan_actual_nombre}} en {{gimnasio_nombre}} vence el {{fecha_vencimiento_formateada}}.\n\n📋 **Plan actual:** {{plan_actual_nombre}} — ${{precio_plan}} {{moneda}}/mes\n📅 **Vigencia hasta:** {{fecha_vencimiento_formateada}}\n✅ **Beneficios incluidos:** {{beneficios_resumen}}\n\n💡 **Oferta especial:** Renueva antes del {{fecha_limite_descuento_formateada}} y obtén un {{descuento_renovacion_porcentaje}}% de descuento.\n\n{{texto_renovacion_automatica}}\n\n¿Deseas renovar? Responde a este correo con \"🔄 Renovar\" o \"📋 Ver opciones\".\n\n¡Te esperamos! 💪\n— {{gimnasio_nombre}}\n```\n\n#### Plantilla (push)\n\n**Título:** 💪 Tu membresía {{plan_actual_nombre}} vence el {{fecha_vencimiento_formateada}}\n**Cuerpo:** Renueva con {{descuento_renovacion_porcentaje}}% de descuento. Toca para renovar.\n\n### Ejemplo renderizado (whatsapp — recordatorio 1)\n\n> ¡Hola Ana García López! 💪 Tu membresía de **Premium** en FitZone Premium vence el **15 de septiembre de 2026**.\n>\n> 📋 **Plan actual:** Premium — $699 MXN/mes\n> ✅ **Beneficios incluidos:** Acceso a gimnasio, cardio, clases grupales ilimitadas, sauna y vapor\n>\n> Renueva antes del **25 de agosto de 2026** y obtén un **5% de descuento**.\n>\n> 🔄 Tu renovación está configurada como automática. Si deseas modificarla, responde \"📋 Ver opciones\".\n>\n> ¿Deseas renovar? Responde \"🔄 Renovar\" o \"📋 Ver opciones\".\n> — FitZone Premium\n\n---\n\n### 1.2 Recordatorio 2 — 15 días antes del vencimiento\n\n#### Plantilla (whatsapp/sms)\n\n```\n¡Hola {{nombre_contacto}}! 💪 Tu membresía de **{{plan_actual_nombre}}** vence en 15 días (**{{fecha_vencimiento_formateada}}**).\n\n💰 **Precio de renovación:** ${{precio_renovacion_formateado}} {{moneda}}/mes\n🎁 **Descuento anticipado:** {{descuento_renovacion_porcentaje}}% si renuevas antes del {{fecha_limite_descuento_formateada}}\n\n¿Deseas renovar? Responde \"🔄 Renovar\" o \"📋 Ver opciones\".\n— {{gimnasio_nombre}}\n```\n\n#### Plantilla (email)\n\n**Asunto:** 15 días para renovar — {{plan_actual_nombre}} — {{gimnasio_nombre}}\n\n```\nHola {{nombre_contacto}},\n\nTu membresía de {{plan_actual_nombre}} en {{gimnasio_nombre}} vence en 15 días ({{fecha_vencimiento_formateada}}).\n\n💰 **Precio de renovación:** ${{precio_renovacion_formateado}} {{moneda}}/mes\n🎁 **Descuento anticipado:** {{descuento_renovacion_porcentaje}}% si renuevas antes del {{fecha_limite_descuento_formateada}}\n\nResponde a este correo con \"🔄 Renovar\" para renovar.\n\n¡Te esperamos! 💪\n— {{gimnasio_nombre}}\n```\n\n### Ejemplo renderizado (whatsapp — recordatorio 2)\n\n> ¡Hola Ana García López! 💪 Tu membresía de **Premium** vence en 15 días (**15 de septiembre de 2026**).\n>\n> 💰 **Precio de renovación:** $699 MXN/mes\n> 🎁 **Descuento anticipado:** 5% si renuevas antes del 25 de agosto de 2026\n>\n> ¿Deseas renovar? Responde \"🔄 Renovar\" o \"📋 Ver opciones\".\n> — FitZone Premium\n\n---\n\n### 1.3 Recordatorio 3 — 7 días antes del vencimiento\n\n#### Plantilla (whatsapp/sms)\n\n```\n¡Hola {{nombre_contacto}}! ⏰ Tu membresía de **{{plan_actual_nombre}}** vence en 7 días (**{{fecha_vencimiento_formateada}}**).\n\n🔄 **Renovación automática:** {{texto_estado_renovacion_automatica}}\n💰 **Precio:** ${{precio_plan}} {{moneda}}/mes\n\nRenueva ahora para continuar disfrutando de tus beneficios.\n\nResponde \"🔄 Renovar\" para renovar con un clic.\n— {{gimnasio_nombre}}\n```\n\n#### Plantilla (email)\n\n**Asunto:** 7 días para renovar — {{plan_actual_nombre}} — {{gimnasio_nombre}}\n\n```\nHola {{nombre_contacto}},\n\nTu membresía de {{plan_actual_nombre}} en {{gimnasio_nombre}} vence en 7 días ({{fecha_vencimiento_formateada}}).\n\n💰 **Precio:** ${{precio_plan}} {{moneda}}/mes\n🔄 **Renovación automática:** {{texto_estado_renovacion_automatica}}\n\nRenueva ahora para continuar disfrutando de tus beneficios.\n\nResponde a este correo con \"🔄 Renovar\" para renovar.\n\n¡Te esperamos! 💪\n— {{gimnasio_nombre}}\n```\n\n### Ejemplo renderizado (whatsapp — recordatorio 3)\n\n> ¡Hola Ana García López! ⏰ Tu membresía de **Premium** vence en 7 días (**15 de septiembre de 2026**).\n>\n> 🔄 **Renovación automática:** Tu membresía se renovará automáticamente el 15 de septiembre.\n> 💰 **Precio:** $699 MXN/mes\n>\n> Renueva ahora para continuar disfrutando de tus beneficios.\n>\n> Responde \"🔄 Renovar\" para renovar con un clic.\n> — FitZone Premium\n\n---\n\n### 1.4 Recordatorio 4 — 3 días antes del vencimiento\n\n#### Plantilla (whatsapp/sms)\n\n```\n¡Hola {{nombre_contacto}}! 🔔 Tu membresía de **{{plan_actual_nombre}}** vence en 3 días (**{{fecha_vencimiento_formateada}}**).\n\n{{texto_recordatorio_final}}\n\n💰 **Precio:** ${{precio_plan}} {{moneda}}/mes\n\nResponde \"🔄 Renovar\" para renovar ahora.\n— {{gimnasio_nombre}}\n```\n\n#### Plantilla (email)\n\n**Asunto:** Última llamada — {{plan_actual_nombre}} vence en 3 días — {{gimnasio_nombre}}\n\n```\nHola {{nombre_contacto}},\n\nTu membresía de {{plan_actual_nombre}} en {{gimnasio_nombre}} vence en 3 días ({{fecha_vencimiento_formateada}}).\n\n{{texto_recordatorio_final}}\n\n💰 **Precio:** ${{precio_plan}} {{moneda}}/mes\n\nResponde a este correo con \"🔄 Renovar\" para renovar ahora.\n\n¡Te esperamos! 💪\n— {{gimnasio_nombre}}\n```\n\n### Ejemplo renderizado (whatsapp — recordatorio 4)\n\n> ¡Hola Ana García López! 🔔 Tu membresía de **Premium** vence en 3 días (**15 de septiembre de 2026**).\n>\n> 🔄 Tu renovación automática se procesará el 15 de septiembre por $699 MXN.\n>\n> 💰 **Precio:** $699 MXN/mes\n>\n> Responde \"🔄 Renovar\" para renovar ahora.\n> — FitZone Premium\n\n---\n\n## 2. Mensaje de seguimiento (recordatorio 2) — Sin respuesta al primer recordatorio\n\nSe envía cuando el miembro no respondió al recordatorio principal dentro del plazo de 72 horas.\n\n#### Plantilla (whatsapp/sms)\n\n```\n¡Hola {{nombre_contacto}}! 💪 Te recordamos que tu membresía de **{{plan_actual_nombre}}** vence el **{{fecha_vencimiento_formateada}}**.\n\n💰 **Precio:** ${{precio_plan}} {{moneda}}/mes\n🔄 **Renovación automática:** {{texto_estado_renovacion_automatica}}\n\n¿Deseas renovar o tienes alguna pregunta? Responde \"🔄 Renovar\" o escríbenos.\n— {{gimnasio_nombre}}\n```\n\n### Ejemplo renderizado (whatsapp — seguimiento)\n\n> ¡Hola Ana García López! 💪 Te recordamos que tu membresía de **Premium** vence el **15 de septiembre de 2026**.\n>\n> 💰 **Precio:** $699 MXN/mes\n> 🔄 **Renovación automática:** Tu membresía se renovará automáticamente.\n>\n> ¿Deseas renovar o tienes alguna pregunta? Responde \"🔄 Renovar\" o escríbenos.\n> — FitZone Premium\n\n---\n\n## 3. Notificación de decisión del miembro al equipo interno\n\n### Plantilla (whatsapp_interno)\n\n```\n📋 DECISIÓN DE RENOVACIÓN — {{gimnasio_nombre}}\n==========================================\n\n**Miembro:** {{nombre_contacto}}\n**Teléfono:** {{telefono_contacto}}\n**Canal:** {{canal_preferido}}\n\n📋 **Plan actual:** {{plan_actual_nombre}} — ${{precio_plan}} {{moneda}}/mes\n📅 **Vence:** {{fecha_vencimiento_formateada}}\n💰 **Precio renovación:** ${{precio_renovacion}} {{moneda}}/mes\n\n{{texto_decision_interno}}\n\n🔗 Registro: {{recordatorio_id}}\n```\n\n### Plantilla (email_interno)\n\n**Asunto:** Decisión de renovación — {{nombre_contacto}} — {{plan_actual_nombre}} — {{gimnasio_nombre}}\n\n```\nDecisión de renovación registrada en {{gimnasio_nombre}}.\n\nMiembro: {{nombre_contacto}}\nTeléfono: {{telefono_contacto}}\nCanal: {{canal_preferido}}\n\nPlan actual: {{plan_actual_nombre}} — ${{precio_plan}} {{moneda}}/mes\nVence: {{fecha_vencimiento_formateada}}\nPrecio renovación: ${{precio_renovacion}} {{moneda}}/mes\n\n{{texto_decision_interno}}\n\nRegistro: {{recordatorio_id}}\n```\n\n### Ejemplo renderizado (whatsapp_interno — miembro renueva)\n\n> 📋 DECISIÓN DE RENOVACIÓN — FitZone Premium\n> ==========================================\n>\n> **Miembro:** Ana García López\n> **Teléfono:** +52 55 9876 5432\n> **Canal:** whatsapp\n>\n> 📋 **Plan actual:** Premium — $699 MXN/mes\n> 📅 **Vence:** 15 de septiembre de 2026\n> 💰 **Precio renovación:** $664.05 MXN/mes (con 5% descuento)\n>\n> ✅ **Miembro confirmó renovación.** Procesar renovación automática con descuento del 5%.\n>\n> 🔗 Registro: MRR-20260816-100000-001\n\n### Ejemplo renderizado (whatsapp_interno — miembro no renueva)\n\n> 📋 DECISIÓN DE RENOVACIÓN — FitZone Premium\n> ==========================================\n>\n> **Miembro:** Laura Hernández\n> **Teléfono:** +52 55 1122 3344\n> **Canal:** whatsapp\n>\n> 📋 **Plan actual:** Elite — $999 MXN/mes\n> 📅 **Vence:** 15 de septiembre de 2026\n> 💰 **Precio renovación:** $999 MXN/mes\n>\n> ❌ **Miembro indicó que no renovará.** Motivo: \"Ya no puedo asistir, me mude de zona\". Gestionar baja y ofrecer alternativas.\n>\n> 🔗 Registro: MRR-20260820-100000-002\n\n---\n\n## 4. Registro de recordatorio completo (JSON)\n\nSe genera al finalizar el paso 6 del flujo para cada recordatorio procesado.\n\n```json\n{\n  \"registro_id\": \"MRRREG-YYYYMMDD-HHMMSS-XXX\",\n  \"recordatorio_id\": \"MRR-YYYYMMDD-HHMMSS-XXX\",\n  \"membresia_id\": \"MEM-YYYYMMDD-XXX\",\n  \"contacto_id\": \"CON-YYYYMMDD-XXX\",\n  \"gimnasio_id\": \"string\",\n  \"gimnasio_nombre\": \"string\",\n  \"nombre_contacto\": \"string\",\n  \"telefono_contacto\": \"string\",\n  \"email_contacto\": \"string | null\",\n  \"canal_preferido\": \"whatsapp | sms | email | app\",\n  \"plan_actual_codigo\": \"basico | premium | elite\",\n  \"plan_actual_nombre\": \"string\",\n  \"precio_plan\": 0.00,\n  \"moneda\": \"string\",\n  \"fecha_vencimiento\": \"YYYY-MM-DD\",\n  \"dias_restantes\": 0,\n  \"descuento_renovacion_porcentaje\": 0,\n  \"fecha_limite_descuento\": \"YYYY-MM-DD\",\n  \"renovacion_automatica\": true | false,\n  \"intervalo_renovacion_dias\": 30 | 15 | 7 | 3,\n  \"momento_recordatorio_calculado\": \"YYYY-MM-DDTHH:MM:SS\",\n  \"momento_recordatorio_ajustado\": \"YYYY-MM-DDTHH:MM:SS\",\n  \"ajuste_recordatorio\": true | false,\n  \"razon_ajuste\": \"string | null\",\n  \"numero_recordatorio\": 1 | 2 | 3 | 4,\n  \"texto_recordatorio_generado\": \"string\",\n  \"fecha_hora_envio\": \"YYYY-MM-DDTHH:MM:SS | null\",\n  \"estado_envio\": \"enviada | error_envio\",\n  \"momento_expiracion_respuesta\": \"YYYY-MM-DDTHH:MM:SS | null\",\n  \"respuesta_contacto\": \"renovado | no_renovara | solicitud_cambio | respuesta_no_clara | sin_respuesta\",\n  \"texto_respuesta\": \"string | null\",\n  \"fecha_hora_respuesta\": \"YYYY-MM-DDTHH:MM:SS | null\",\n  \"duracion_respuesta_horas\": 0.0 | null,\n  \"recordatorio_2_enviado\": true | false,\n  \"fecha_hora_envio_recordatorio_2\": \"YYYY-MM-DDTHH:MM:SS | null\",\n  \"respuesta_contacto_recordatorio_2\": \"renovado_posterior | no_renovara_posterior | sin_respuesta_final | null\",\n  \"resultado\": \"renovado | no_renovara | solicitud_cambio | renovado_posterior | no_renovara_posterior | sin_respuesta_final | respuesta_no_clara | ya_renovada | envio_fallido\",\n  \"fue_escalado\": true | false,\n  \"motivo_escalamiento\": \"string | null\",\n  \"fecha_hora_cierre\": \"YYYY-MM-DDTHH:MM:SS\",\n  \"duracion_proceso_segundos\": 0,\n  \"agente\": \"membership-renewal-reminder\"\n}\n```\n\n### Ejemplo — Miembro renueva en recordatorio 1 (30 días)\n\n```json\n{\n  \"registro_id\": \"MRRREG-20260816-100005-001\",\n  \"recordatorio_id\": \"MRR-20260816-100000-001\",\n  \"membresia_id\": \"MEM-20260115-101\",\n  \"contacto_id\": \"CON-20260115-101\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"nombre_contacto\": \"Ana García López\",\n  \"telefono_contacto\": \"+52 55 9876 5432\",\n  \"email_contacto\": \"ana.garcia@correo.com\",\n  \"canal_preferido\": \"whatsapp\",\n  \"plan_actual_codigo\": \"premium\",\n  \"plan_actual_nombre\": \"Premium\",\n  \"precio_plan\": 699.00,\n  \"moneda\": \"MXN\",\n  \"fecha_vencimiento\": \"2026-09-15\",\n  \"dias_restantes\": 30,\n  \"descuento_renovacion_porcentaje\": 5,\n  \"fecha_limite_descuento\": \"2026-08-25\",\n  \"renovacion_automatica\": true,\n  \"intervalo_renovacion_dias\": 30,\n  \"momento_recordatorio_calculado\": \"2026-08-16\",\n  \"momento_recordatorio_ajustado\": \"2026-08-16T10:00:00\",\n  \"ajuste_recordatorio\": true,\n  \"razon_ajuste\": \"Fecha calculada sin hora específica, ajustado a 10:00\",\n  \"numero_recordatorio\": 1,\n  \"texto_recordatorio_generado\": \"¡Hola Ana García López! 💪 Tu membresía de Premium en FitZone Premium vence el 15 de septiembre de 2026.\\n\\n📋 Plan actual: Premium — $699 MXN/mes\\n✅ Beneficios: Acceso a gimnasio, cardio, clases grupales ilimitadas, sauna y vapor\\n\\nRenueva antes del 25 de agosto de 2026 y obtén un 5% de descuento.\\n\\n¿Deseas renovar? Responde \\\"🔄 Renovar\\\" o \\\"📋 Ver opciones\\\".\\n— FitZone Premium\",\n  \"fecha_hora_envio\": \"2026-08-16T10:00:02\",\n  \"estado_envio\": \"enviada\",\n  \"momento_expiracion_respuesta\": \"2026-08-19T10:00:02\",\n  \"respuesta_contacto\": \"renovado\",\n  \"texto_respuesta\": \"🔄 Renovar\",\n  \"fecha_hora_respuesta\": \"2026-08-16T11:30:00\",\n  \"duracion_respuesta_horas\": 1.5,\n  \"recordatorio_2_enviado\": false,\n  \"fecha_hora_envio_recordatorio_2\": null,\n  \"respuesta_contacto_recordatorio_2\": null,\n  \"resultado\": \"renovado\",\n  \"fue_escalado\": false,\n  \"motivo_escalamiento\": null,\n  \"fecha_hora_cierre\": \"2026-08-16T11:30:03\",\n  \"duracion_proceso_segundos\": 5403,\n  \"agente\": \"membership-renewal-reminder\"\n}\n```\n\n### Ejemplo — Miembro no renueva en recordatorio 3 (7 días)\n\n```json\n{\n  \"registro_id\": \"MRRREG-20260908-100005-002\",\n  \"recordatorio_id\": \"MRR-20260908-100000-002\",\n  \"membresia_id\": \"MEM-20260301-205\",\n  \"contacto_id\": \"CON-20260301-205\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"nombre_contacto\": \"Laura Hernández\",\n  \"telefono_contacto\": \"+52 55 1122 3344\",\n  \"email_contacto\": null,\n  \"canal_preferido\": \"whatsapp\",\n  \"plan_actual_codigo\": \"elite\",\n  \"plan_actual_nombre\": \"Elite\",\n  \"precio_plan\": 999.00,\n  \"moneda\": \"MXN\",\n  \"fecha_vencimiento\": \"2026-09-15\",\n  \"dias_restantes\": 7,\n  \"descuento_renovacion_porcentaje\": 5,\n  \"fecha_limite_descuento\": \"2026-08-25\",\n  \"renovacion_automatica\": true,\n  \"intervalo_renovacion_dias\": 7,\n  \"momento_recordatorio_calculado\": \"2026-09-08\",\n  \"momento_recordatorio_ajustado\": \"2026-09-08T10:00:00\",\n  \"ajuste_recordatorio\": true,\n  \"razon_ajuste\": \"Fecha calculada sin hora específica, ajustado a 10:00\",\n  \"numero_recordatorio\": 3,\n  \"texto_recordatorio_generado\": \"¡Hola Laura Hernández! ⏰ Tu membresía de Elite vence en 7 días (15 de septiembre de 2026).\\n\\n🔄 Renovación automática: Tu membresía se renovará automáticamente el 15 de septiembre.\\n💰 Precio: $999 MXN/mes\\n\\nRenueva ahora para continuar disfrutando de tus beneficios.\\n\\nResponde \\\"🔄 Renovar\\\" para renovar con un clic.\\n— FitZone Premium\",\n  \"fecha_hora_envio\": \"2026-09-08T10:00:01\",\n  \"estado_envio\": \"enviada\",\n  \"momento_expiracion_respuesta\": \"2026-09-11T10:00:01\",\n  \"respuesta_contacto\": \"no_renovara\",\n  \"texto_respuesta\": \"❌ No renovar, ya no puedo asistir por cambio de trabajo\",\n  \"fecha_hora_respuesta\": \"2026-09-08T14:20:00\",\n  \"duracion_respuesta_horas\": 4.33,\n  \"recordatorio_2_enviado\": false,\n  \"fecha_hora_envio_recordatorio_2\": null,\n  \"respuesta_contacto_recordatorio_2\": null,\n  \"resultado\": \"no_renovara\",\n  \"fue_escalado\": false,\n  \"motivo_escalamiento\": null,\n  \"fecha_hora_cierre\": \"2026-09-08T14:20:03\",\n  \"duracion_proceso_segundos\": 15603,\n  \"agente\": \"membership-renewal-reminder\"\n}\n```\n\n### Ejemplo — Sin respuesta, segundo recordatorio enviado\n\n```json\n{\n  \"registro_id\": \"MRRREG-20260831-100005-003\",\n  \"recordatorio_id\": \"MRR-20260831-100000-003\",\n  \"membresia_id\": \"MEM-20250831-301\",\n  \"contacto_id\": \"CON-20250831-301\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"nombre_contacto\": \"Roberto Ruiz Méndez\",\n  \"telefono_contacto\": \"+52 55 4567 8901\",\n  \"email_contacto\": \"roberto.ruiz@correo.com\",\n  \"canal_preferido\": \"whatsapp\",\n  \"plan_actual_codigo\": \"basico\",\n  \"plan_actual_nombre\": \"Básico\",\n  \"precio_plan\": 399.00,\n  \"moneda\": \"MXN\",\n  \"fecha_vencimiento\": \"2026-09-15\",\n  \"dias_restantes\": 15,\n  \"descuento_renovacion_porcentaje\": 5,\n  \"fecha_limite_descuento\": \"2026-08-25\",\n  \"renovacion_automatica\": false,\n  \"intervalo_renovacion_dias\": 15,\n  \"momento_recordatorio_calculado\": \"2026-08-31\",\n  \"momento_recordatorio_ajustado\": \"2026-08-31T10:00:00\",\n  \"ajuste_recordatorio\": true,\n  \"razon_ajuste\": \"Fecha calculada sin hora específica, ajustado a 10:00\",\n  \"numero_recordatorio\": 2,\n  \"texto_recordatorio_generado\": \"¡Hola Roberto Ruiz Méndez! 💪 Tu membresía de Básico vence en 15 días (15 de septiembre de 2026).\\n\\n💰 Precio de renovación: $399 MXN/mes\\n🎁 Descuento anticipado: 5% si renuevas antes del 25 de agosto de 2026\\n\\n¿Deseas renovar? Responde \\\"🔄 Renovar\\\" o \\\"📋 Ver opciones\\\".\\n— FitZone Premium\",\n  \"fecha_hora_envio\": \"2026-08-31T10:00:02\",\n  \"estado_envio\": \"enviada\",\n  \"momento_expiracion_respuesta\": \"2026-09-03T10:00:02\",\n  \"respuesta_contacto\": \"sin_respuesta\",\n  \"texto_respuesta\": null,\n  \"fecha_hora_respuesta\": null,\n  \"duracion_respuesta_horas\": null,\n  \"recordatorio_2_enviado\": true,\n  \"fecha_hora_envio_recordatorio_2\": \"2026-09-03T10:00:03\",\n  \"respuesta_contacto_recordatorio_2\": \"sin_respuesta_final\",\n  \"resultado\": \"sin_respuesta_final\",\n  \"fue_escalado\": false,\n  \"motivo_escalamiento\": null,\n  \"fecha_hora_cierre\": \"2026-09-15T10:00:00\",\n  \"duracion_proceso_segundos\": 1209598,\n  \"agente\": \"membership-renewal-reminder\"\n}\n```\n\n---\n\n## 5. Registro de escalamiento a humano (JSON)\n\nSe genera cuando se activa una condición de escalamiento (ver `reglas-escalamiento.md`).\n\n```json\n{\n  \"registro_id\": \"MRRREG-YYYYMMDD-HHMMSS-XXX\",\n  \"recordatorio_id\": \"MRR-YYYYMMDD-HHMMSS-XXX\",\n  \"evento\": \"escalamiento\",\n  \"fecha_hora_ejecucion\": \"YYYY-MM-DDTHH:MM:SS\",\n  \"agente\": \"membership-renewal-reminder\",\n  \"gimnasio_id\": \"string\",\n  \"gimnasio_nombre\": \"string\",\n  \"nombre_contacto\": \"string\",\n  \"telefono_contacto\": \"string\",\n  \"email_contacto\": \"string | null\",\n  \"canal_preferido\": \"whatsapp | sms | email | app\",\n  \"membresia_id\": \"string\",\n  \"plan_actual_codigo\": \"basico | premium | elite\",\n  \"plan_actual_nombre\": \"string\",\n  \"fecha_vencimiento\": \"YYYY-MM-DD\",\n  \"resultado\": \"escalamiento\",\n  \"detalles\": \"Motivo: {{motivo_escalamiento}}.\",\n  \"responsable_escalamiento\": \"string nombre del responsable\",\n  \"canal_escalamiento\": \"whatsapp_interno | email_interno\",\n  \"plazo_respuesta_horas\": 0,\n  \"datos_origen\": {\n    \"recordatorio_id\": \"{{recordatorio_id}}\",\n    \"membresia_id\": \"{{membresia_id}}\",\n    \"nombre_contacto\": \"{{nombre_contacto}}\",\n    \"telefono_contacto\": \"{{telefono_contacto}}\",\n    \"plan_actual_nombre\": \"{{plan_actual_nombre}}\",\n    \"fecha_vencimiento\": \"{{fecha_vencimiento}}\"\n  }\n}\n```\n\n### Ejemplo — Fallo de envío\n\n```json\n{\n  \"registro_id\": \"MRRREG-20260816-100035-004\",\n  \"recordatorio_id\": \"MRR-20260816-100000-004\",\n  \"evento\": \"escalamiento\",\n  \"fecha_hora_ejecucion\": \"2026-08-16T10:00:35\",\n  \"agente\": \"membership-renewal-reminder\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"nombre_contacto\": \"María López\",\n  \"telefono_contacto\": \"+52 55 5566 7788\",\n  \"email_contacto\": null,\n  \"canal_preferido\": \"whatsapp\",\n  \"membresia_id\": \"MEM-20260801-401\",\n  \"plan_actual_codigo\": \"premium\",\n  \"plan_actual_nombre\": \"Premium\",\n  \"fecha_vencimiento\": \"2026-09-15\",\n  \"resultado\": \"escalamiento\",\n  \"detalles\": \"Motivo: Error de envío por canal whatsapp tras 2 intentos fallidos al enviar recordatorio 1 de renovación a María López (membresía MEM-20260801-401, vence 2026-09-15).\",\n  \"responsable_escalamiento\": \"Carlos Mendoza\",\n  \"canal_escalamiento\": \"whatsapp_interno\",\n  \"plazo_respuesta_horas\": 2,\n  \"datos_origen\": {\n    \"recordatorio_id\": \"MRR-20260816-100000-004\",\n    \"membresia_id\": \"MEM-20260801-401\",\n    \"nombre_contacto\": \"María López\",\n    \"telefono_contacto\": \"+52 55 5566 7788\",\n    \"plan_actual_nombre\": \"Premium\",\n    \"fecha_vencimiento\": \"2026-09-15\"\n  }\n}\n```\n\n---\n\n## 6. Formato de fecha para mensajes al contacto\n\n| Elemento | Formato | Ejemplo |\n|---|---|---|\n| `fecha_vencimiento_formateada` | Día + mes + año en español | `\"15 de septiembre de 2026\"` |\n| `fecha_limite_descuento_formateada` | Día + mes + año en español | `\"25 de agosto de 2026\"` |\n\n### Tabla de meses\n\n| Mes | Formato |\n|---|---|\n| Enero | `\"enero\"` |\n| Febrero | `\"febrero\"` |\n| Marzo | `\"marzo\"` |\n| Abril | `\"abril\"` |\n| Mayo | `\"mayo\"` |\n| Junio | `\"junio\"` |\n| Julio | `\"julio\"` |\n| Agosto | `\"agosto\"` |\n| Septiembre | `\"septiembre\"` |\n| Octubre | `\"octubre\"` |\n| Noviembre | `\"noviembre\"` |\n| Diciembre | `\"diciembre\"` |\n"
              },
              {
                "id": "membership-renewal-reminder/prompt-sistema.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "prompt-sistema.md",
                "contenido": "# Prompt del sistema — Membership Renewal Reminder\n\n## Identidad\nEres **FitBot**, asistente virtual de **GymPro Fitness Center**. Tu función es enviar recordatorios automáticos antes de la fecha de vencimiento o renovación de membresías de forma profesional y orientada al beneficio del miembro.\n\n## Tono de comunicación\n- **Profesional y cercano**: el miembro es parte de la comunidad fitness. Tratarlo como alguien valioso, no como un número.\n- **Informativo, no presionante**: presentar la renovación como una oportunidad para continuar con los beneficios, no como una obligación.\n- **Breve pero completo**: máximo 4 párrafos por mensaje. Incluir fecha de vencimiento, precio, beneficios y opciones de acción.\n- **Sin generar urgencia artificial**: no usar frases como \"se vence tu membresía\" o \"pierde tus beneficios\". Usar \"tu membresía vence el\" y \"renueva para continuar disfrutando\".\n- **Personalizado**: incluir el nombre del miembro, su plan actual y sus beneficios.\n\n## Flujo de ejecución\nEjecuta los siguientes pasos en orden exacto, sin omitir ni reordenar:\n\n### Paso 1 — Consulta de la fecha/hora del evento o cita asociado al contacto\nConsulta el sistema del gimnasio para obtener los datos de la membresía del contacto que está próxima a vencer.\n\n### Paso 2 — Cálculo del momento de envío según reglas fijas\nCalcula el momento exacto en que debe enviar cada recordatorio, aplicando las reglas de tiempo configuradas (30 días, 15 días, 7 días y 3 días antes de la fecha de vencimiento).\n\n### Paso 3 — Generación del mensaje de recordatorio con datos concretos del evento\nGenera el texto del mensaje de recordatorio usando la plantilla correspondiente según el número de recordatorio.\n\n### Paso 4 — Envío del mensaje por el canal configurado\nEnvía el mensaje de recordatorio al contacto por el canal configurado.\n\n### Paso 5 — Registro de la confirmación o falta de respuesta del contacto\nRegistra la respuesta del contacto o la ausencia de respuesta dentro del plazo definido.\n\n### Paso 6 — Disparo de segundo recordatorio automático si no hay respuesta\nSi el contacto no respondió al primer recordatorio dentro del plazo, envía un segundo recordatorio automático.\n\n## Ejemplo de ejecución\n\n**Entrada:**\n- nombre_cliente: \"Ana García López\"\n- telefono: \"+52-55-9876-5432\"\n- membresia: \"Premium, vence el 15 de septiembre de 2026\"\n\n**Salida generada (paso 3 — recordatorio 30 días antes):**\n```\n¡Hola Ana García! 💪 Tu membresía de Premium en FitZone Premium vence el 15 de septiembre de 2026.\n\n📋 Plan actual: Premium — $699 MXN/mes\n✅ Beneficios incluidos: Acceso a gimnasio, cardio, clases grupales ilimitadas, sauna y vapor\n\nRenueva antes del 25 de agosto de 2026 y obtén un 5% de descuento.\n\n¿Deseas renovar? Responde \"🔄 Renovar\" o \"📋 Ver opciones\".\n```\n\n## Tipos de recordatorio de renovación\n\n| ID | Intervalo | Días antes del vencimiento | Tipo de mensaje |\n|---|---|---|---|\n| `RENOVACION_30_DIAS` | 30 días antes | 30 | Informativo + oferta de descuento |\n| `RENOVACION_15_DIAS` | 15 días antes | 15 | Informativo + beneficios |\n| `RENOVACION_7_DIAS` | 7 días antes | 7 | Recordatorio de vencimiento |\n| `RENOVACION_3_DIAS` | 3 días antes | 3 | Última oportunidad |\n\n## Restricciones\n- No enviar recordatorios fuera del horario de atención (antes de las 08:00 o después de las 22:00).\n- No compartir información de otros miembros. Cada recordatorio es independiente.\n- Máximo 2 mensajes por intervalo de recordatorio.\n- Si la membresía ya fue renovada después de programar el recordatorio, no enviar el recordatorio.\n\n## Reglas de escalamiento\n- Si el envío del recordatorio falla 2 veces consecutivas → escalar a humano.\n- Si el miembro responde con una pregunta no relacionada → escalar a humano.\n- Si el miembro solicita cambiar de plan → escalar a humano.\n- Si la membresía ya fue renovada o cancelada → escalar a humano.\n- Tiempo máximo de respuesta: 2 minutos. Si excede, escalar a equipo humano.\n"
              },
              {
                "id": "membership-renewal-reminder/reglas-escalamiento.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "reglas-escalamiento.md",
                "contenido": "# Reglas de escalamiento — Membership Renewal Reminder\n\nEste archivo define la lista cerrada y exacta de condiciones bajo las cuales el agente **debe** transferir el caso a un humano (asesor de membresías, gerente o responsable de atención al cliente). No existe discrecionalidad: si se cumple alguna condición, el escalamiento es **obligatorio**.\n\n---\n\n## Condiciones de escalamiento\n\n### C1 — Datos del contacto o membresía inválidos\n\n| # | Condición | Acción |\n|---|---|---|\n| C1.1 | El `membresia_id` tiene formato inválido o está vacío | Escalamiento obligatorio. Motivo: `ID de membresía inválido o vacío. No se puede generar recordatorio de renovación.` No se procesa el recordatorio. |\n| C1.2 | El `contacto_id` tiene formato inválido o está vacío | Escalamiento obligatorio. Motivo: `ID de contacto inválido o vacío asociado a la membresía {{membresia_id}}.` No se procesa el recordatorio. |\n| C1.3 | El `nombre_contacto` está vacío, es `null` o es genérico (ej. \"Miembro\", \"Sin nombre\") | Escalamiento obligatorio. Motivo: `Nombre del contacto no disponible o genérico. No se puede personalizar el recordatorio de renovación para la membresía {{membresia_id}}.` No se procesa el recordatorio. |\n| C1.4 | El `telefono_contacto` tiene menos de 8 dígitos o más de 15 dígitos y `canal_preferido` es `whatsapp` o `sms` | Escalamiento obligatorio. Motivo: `Teléfono del contacto '{{telefono_contacto}}' no cumple con el formato válido (8-15 dígitos). No es posible enviar recordatorio por {{canal_preferido}}.` No se envía recordatorio. |\n| C1.5 | El `canal_preferido` es `email` pero el `email_contacto` es `null` o está vacío | Escalamiento obligatorio. Motivo: `Canal preferido configurado como 'email' pero el contacto no tiene email registrado. No es posible enviar recordatorio.` No se envía recordatorio. |\n| C1.6 | El `email_contacto` está presente pero no tiene formato válido de email | Escalamiento obligatorio. Motivo: `Email del contacto '{{email_contacto}}' no tiene formato válido. No es posible enviar recordatorio por email.` No se envía recordatorio. |\n| C1.7 | El `estado_membresia` no es `activa` (es `cancelada`, `vencida` o `suspendida`) | No se procesa recordatorio. **No escala.** Se registra como `membresia_no_activa`. No se genera registro de escalamiento. |\n| C1.8 | La `fecha_vencimiento` es anterior a la `fecha_actual` | No se procesa recordatorio. **No escala.** Se descarta. Motivo: `Membresía ya vencida ({{fecha_vencimiento}}). Fuera del umbral de procesamiento.` No se genera registro. |\n\n---\n\n### C2 — Datos de la membresía o plan incompletos\n\n| # | Condición | Acción |\n|---|---|---|\n| C2.1 | El `plan_actual_codigo` no es uno de los 3 valores válidos (`basico`, `premium`, `elite`) | Escalamiento obligatorio. Motivo: `Código de plan '{{plan_actual_codigo}}' no reconocido para la membresía {{membresia_id}}. No se puede generar recordatorio personalizado.` No se procesa el recordatorio. |\n| C2.2 | El `plan_actual_nombre` es `null` o está vacío | Escalamiento obligatorio. Motivo: `Nombre del plan no disponible para la membresía {{membresia_id}}. No se puede incluir en el recordatorio.` No se procesa el recordatorio. |\n| C2.3 | El `precio_plan` es `null`, 0 o negativo | Escalamiento obligatorio. Motivo: `Precio del plan '{{precio_plan}}' no válido para la membresía {{membresia_id}}. No se puede incluir información de precios en el recordatorio.` No se procesa el recordatorio. |\n| C2.4 | La `fecha_vencimiento` tiene formato inválido o es `null` | Escalamiento obligatorio. Motivo: `Fecha de vencimiento no disponible o con formato inválido para la membresía {{membresia_id}}.` No se procesa el recordatorio. |\n| C2.5 | El `beneficios_plan` está vacío o es `null` | Escalamiento obligatorio. Motivo: `Beneficios del plan no disponibles para la membresía {{membresia_id}}. No se puede generar recordatorio personalizado.` No se procesa el recordatorio. |\n\n---\n\n### C3 — Fallo técnico de envío\n\n| # | Condición | Acción |\n|---|---|---|\n| C3.1 | El envío del recordatorio falla **2 veces consecutivas** (intento original + 1 reintento tras 30 segundos) | Escalamiento obligatorio. Motivo: `Error de envío por canal {{canal_preferido}} tras 2 intentos fallidos al enviar recordatorio de renovación a {{nombre_contacto}} (membresía {{membresia_id}}, vence {{fecha_vencimiento}}).` Generar registro de escalamiento. Notificar al equipo interno para contacto directo. Plazo: **2 horas**. |\n| C3.2 | El `canal_preferido` no es uno de los 4 valores válidos (`whatsapp`, `sms`, `email`, `app`) | Escalamiento obligatorio. Motivo: `Canal preferido '{{canal_preferido}}' no reconocido para la membresía {{membresia_id}}.` No se procesa el recordatorio. |\n| C3.3 | El destino del envío (número de teléfono, email o token) está vacío o es inválido | Escalamiento obligatorio. Motivo: `Destino de envío inválido o vacío para canal {{canal_preferido}}.` No se envía recordatorio. |\n\n---\n\n### C4 — Respuesta del contacto no procesable\n\n| # | Condición | Acción |\n|---|---|---|\n| C4.1 | El contacto responde con una pregunta o mensaje que no se clasifica como renovación ni no-renovación (contiene \"?\" o no coincide con los textos base) | Escalamiento obligatorio. Motivo: `Respuesta del contacto no clasificable como renovación o no-renovación. Texto: '{{texto_respuesta}}'. Requiere intervención humana para asesoría de membresía.` Generar registro de escalamiento. Notificar al equipo interno. Plazo: **4 horas**. |\n| C4.2 | El contacto indica que quiere cambiar de plan (ej. \"Quiero otro plan\", \"¿Puedo bajar?\", \"Cambiar a Básico\") | Escalamiento obligatorio. Motivo: `Contacto solicita cambio de plan: '{{texto_respuesta}}'. Requiere asesoría humana para verificar opciones de downgrade/upgrade y políticas de cambio.` Generar registro de escalamiento. Notificar al equipo interno. Plazo: **4 horas**. |\n| C4.3 | El contacto indica un problema con el cobro o la facturación (ej. \"Me cobraron de más\", \"No reconocí el cargo\", \"Error en el pago\") | Escalamiento obligatorio. Motivo: `Contacto reporta problema de cobro o facturación: '{{texto_respuesta}}'. Requiere intervención del equipo de cobranza.` Generar registro de escalamiento. Notificar al equipo interno. Plazo: **4 horas**. |\n| C4.4 | El contacto indica un problema con el servicio o instalaciones (ej. \"No me gusta el gym\", \"Las máquinas están rotas\", \"El servicio es malo\") | Escalamiento obligatorio. Motivo: `Contacto reporta insatisfacción con el servicio: '{{texto_respuesta}}'. Requiere seguimiento humano para gestión de retención.` Generar registro de escalamiento. Notificar al equipo interno. Plazo: **4 horas**. |\n\n---\n\n### C5 — Restricciones de configuración del gimnasio\n\n| # | Condición | Acción |\n|---|---|---|\n| C5.1 | El `gimnasio_telefono` no está configurado o está vacío | Escalamiento obligatorio. Motivo: `Teléfono del gimnasio no configurado. No se puede incluir información de contacto en el recordatorio de renovación de la membresía {{membresia_id}}.` No se envía recordatorio. |\n| C5.2 | El `gimnasio_direccion` no está configurado o está vacío | Escalamiento obligatorio. Motivo: `Dirección del gimnasio no configurada. No se puede incluir ubicación en el recordatorio de renovación.` No se envía recordatorio. |\n| C5.3 | El `descuento_renovacion_porcentaje` es mayor a 50 | Escalamiento obligatorio. Motivo: `Porcentaje de descuento '{{descuento_renovacion_porcentaje}}%' excede el límite máximo (50%). Verificar configuración de descuentos.` No se envía recordatorio con descuento inválido. |\n| C5.4 | El `zona_horaria` no está configurada o no es una zona horaria IANA válida | **No escala.** Usar `America/Mexico_City` como valor por defecto. Registrar advertencia. |\n\n---\n\n### C6 — Renovación automática con problemas\n\n| # | Condición | Acción |\n|---|---|---|\n| C6.1 | La membresía tiene `renovacion_automatica: true` pero el contacto indica que NO renovará (respuesta `no_renovara` o `no_renovara_posterior`) | Notificar al equipo interno **inmediatamente**. Motivo: `Miembro con renovación automática activa indicó que NO renovará. Se requiere desactivar la renovación automática antes del {{fecha_vencimiento}} para evitar cobro no deseado. Contacto: {{nombre_contacto}} ({{telefono_contacto}}).` Generar notificación al equipo interno con formato de `formato-salida.md` → sección 3. **No genera registro de escalamiento.** Es notificación urgente, no transferencia de caso. |\n| C6.2 | La membresía tiene `renovacion_automatica: true` pero no hay método de pago registrado en el sistema | Escalamiento obligatorio. Motivo: `Membresía con renovación automática activa pero sin método de pago registrado. La renovación automática fallará el {{fecha_vencimiento}}. Contacto: {{nombre_contacto}} ({{telefono_contacto}}).` Generar registro de escalamiento. Notificar al equipo interno para coordinar actualización de datos de pago. Plazo: **2 horas**. |\n\n---\n\n## Resumen de tiempos de escalamiento\n\n| Condición | Tiempo antes de escalar | Prioridad |\n|---|---|---|\n| Membresía_id inválido (C1.1) | 0 segundos (inmediato) | Normal |\n| Contacto_id inválido (C1.2) | 0 segundos (inmediato) | Normal |\n| Nombre genérico (C1.3) | 0 segundos (inmediato) | Normal |\n| Teléfono inválido (C1.4) | 0 segundos (inmediato) | Normal |\n| Email faltante (C1.5) | 0 segundos (inmediato) | Normal |\n| Email inválido (C1.6) | 0 segundos (inmediato) | Normal |\n| Plan no reconocido (C2.1) | 0 segundos (inmediato) | Normal |\n| Plan sin nombre (C2.2) | 0 segundos (inmediato) | Normal |\n| Precio inválido (C2.3) | 0 segundos (inmediato) | Normal |\n| Fecha vencimiento inválida (C2.4) | 0 segundos (inmediato) | Normal |\n| Sin beneficios (C2.5) | 0 segundos (inmediato) | Normal |\n| Fallo envío recordatorio (C3.1) | 0 segundos tras 1 reintento fallido | Normal |\n| Canal no reconocido (C3.2) | 0 segundos (inmediato) | Normal |\n| Destino inválido (C3.3) | 0 segundos (inmediato) | Normal |\n| Respuesta no procesable (C4.1) | 0 segundos (inmediato) | Normal |\n| Solicitud cambio plan (C4.2) | 0 segundos (inmediato) | Normal |\n| Problema cobro (C4.3) | 0 segundos (inmediato) | Normal |\n| Insatisfacción servicio (C4.4) | 0 segundos (inmediato) | Normal |\n| Teléfono gimnasio no configurado (C5.1) | 0 segundos (inmediato) | Normal |\n| Dirección gimnasio no configurada (C5.2) | 0 segundos (inmediato) | Normal |\n| Descuento inválido (C5.3) | 0 segundos (inmediato) | Normal |\n| Zona horaria inválida (C5.4) | N/A (usa default) | N/A |\n| Renovación auto + no renovará (C6.1) | 0 segundos (inmediato) | **Alta** (notificación, no escalamiento) |\n| Renovación auto sin pago (C6.2) | 0 segundos (inmediato) | **Alta** |\n\n---\n\n## Plazos de respuesta del equipo interno\n\n| Tipo de escalamiento | Plazo de respuesta esperado |\n|---|---|\n| Renovación auto sin pago registrado (C6.2) | 2 horas |\n| Fallo de envío recordatorio (C3.1) | 2 horas |\n| Respuesta no procesable del contacto (C4.1) | 4 horas |\n| Solicitud de cambio de plan (C4.2) | 4 horas |\n| Problema de cobro/facturación (C4.3) | 4 horas |\n| Insatisfacción con servicio (C4.4) | 4 horas |\n| Datos de membresía inválidos (C1.1–C1.6, C2.1–C2.5) | 4 horas |\n| Canal no reconocido (C3.2) | 4 horas |\n| Gimnasio sin teléfono/dirección (C5.1, C5.2) | 4 horas |\n| Descuento inválido (C5.3) | 4 horas |\n\n---\n\n## Proceso de escalamiento\n\nCuando se activa cualquiera de las condiciones C1–C5:\n\n1. El agente genera el registro de escalamiento con `resultado: \"escalamiento\"` usando el formato de `formato-salida.md` → sección 5.\n2. El agente envía al contacto el mensaje de escalamiento (solo para C4.1–C4.4) según `formato-salida.md` (sección similar a la de otros agentes de gimnasio).\n3. El agente notifica al equipo interno con los datos del registro de escalamiento.\n4. El agente actualiza `resultado` según corresponda.\n5. El agente espera la resolución del equipo interno dentro del plazo correspondiente.\n6. Si el equipo interno no responde dentro del plazo, el agente notifica al responsable con nivel de urgencia correspondiente.\n\n**Nota:** Para C6.1 (renovación auto + miembro no renueva), el proceso es diferente: se genera una **notificación urgente** al equipo interno (no un escalamiento) con el formato de `formato-salida.md` → sección 3. No se genera registro de escalamiento ni se espera resolución formal, pero se marca como urgente para evitar cobro no deseado.\n\n---\n\n## Textos base de escalamiento\n\nLos siguientes textos se usan literalmente en los registros de escalamiento. No se deben modificar ni parafrasear.\n\n| Condición | Texto del campo `detalles` |\n|---|---|\n| C1.1 | `\"Motivo: ID de membresía inválido o vacío. No se puede generar recordatorio de renovación.\"` |\n| C1.2 | `\"Motivo: ID de contacto inválido o vacío asociado a la membresía {{membresia_id}}.\"` |\n| C1.3 | `\"Motivo: Nombre del contacto no disponible o genérico. No se puede personalizar el recordatorio de renovación para la membresía {{membresia_id}}.\"` |\n| C1.4 | `\"Motivo: Teléfono del contacto '{{telefono_contacto}}' no cumple con el formato válido (8-15 dígitos). No es posible enviar recordatorio por {{canal_preferido}}.\"` |\n| C1.5 | `\"Motivo: Canal preferido configurado como 'email' pero el contacto no tiene email registrado. No es posible enviar recordatorio.\"` |\n| C1.6 | `\"Motivo: Email del contacto '{{email_contacto}}' no tiene formato válido. No es posible enviar recordatorio por email.\"` |\n| C2.1 | `\"Motivo: Código de plan '{{plan_actual_codigo}}' no reconocido para la membresía {{membresia_id}}. No se puede generar recordatorio personalizado.\"` |\n| C2.2 | `\"Motivo: Nombre del plan no disponible para la membresía {{membresia_id}}. No se puede incluir en el recordatorio.\"` |\n| C2.3 | `\"Motivo: Precio del plan '{{precio_plan}}' no válido para la membresía {{membresia_id}}. No se puede incluir información de precios en el recordatorio.\"` |\n| C2.4 | `\"Motivo: Fecha de vencimiento no disponible o con formato inválido para la membresía {{membresia_id}}.\"` |\n| C2.5 | `\"Motivo: Beneficios del plan no disponibles para la membresía {{membresia_id}}. No se puede generar recordatorio personalizado.\"` |\n| C3.1 | `\"Motivo: Error de envío por canal {{canal_preferido}} tras 2 intentos fallidos al enviar recordatorio de renovación a {{nombre_contacto}} (membresía {{membresia_id}}, vence {{fecha_vencimiento}}).\"` |\n| C3.2 | `\"Motivo: Canal preferido '{{canal_preferido}}' no reconocido para la membresía {{membresia_id}}.\"` |\n| C3.3 | `\"Motivo: Destino de envío inválido o vacío para canal {{canal_preferido}}.\"` |\n| C4.1 | `\"Motivo: Respuesta del contacto no clasificable como renovación o no-renovación. Texto: '{{texto_respuesta}}'. Requiere intervención humana para asesoría de membresía.\"` |\n| C4.2 | `\"Motivo: Contacto solicita cambio de plan: '{{texto_respuesta}}'. Requiere asesoría humana para verificar opciones de downgrade/upgrade y políticas de cambio.\"` |\n| C4.3 | `\"Motivo: Contacto reporta problema de cobro o facturación: '{{texto_respuesta}}'. Requiere intervención del equipo de cobranza.\"` |\n| C4.4 | `\"Motivo: Contacto reporta insatisfacción con el servicio: '{{texto_respuesta}}'. Requiere seguimiento humano para gestión de retención.\"` |\n| C5.1 | `\"Motivo: Teléfono del gimnasio no configurado. No se puede incluir información de contacto en el recordatorio de renovación de la membresía {{membresia_id}}.\"` |\n| C5.2 | `\"Motivo: Dirección del gimnasio no configurada. No se puede incluir ubicación en el recordatorio de renovación.\"` |\n| C5.3 | `\"Motivo: Porcentaje de descuento '{{descuento_renovacion_porcentaje}}%' excede el límite máximo (50%). Verificar configuración de descuentos.\"` |\n| C6.2 | `\"Motivo: Membresía con renovación automática activa pero sin método de pago registrado. La renovación automática fallará el {{fecha_vencimiento}}. Contacto: {{nombre_contacto}} ({{telefono_contacto}}).\"` |\n\n---\n\n## Ejemplo completo de activación\n\n**Condición activada:** C4.4 (contacto reporta insatisfacción con el servicio)\n\n**Contexto:** El miembro Roberto Ruiz Méndez recibió el recordatorio 2 (15 días antes) de su membresía Básico. Respondió: \"Las máquinas de cardio estuvieron rotas toda la semana, no voy a renovar si no arreglan eso\".\n\n**Flujo del agente:**\n1. Recibe respuesta del contacto.\n2. Clasifica como `no_renovara` + detecta insatisfacción con el servicio.\n3. Activa escalamiento obligatorio (C4.4).\n4. Envía respuesta al contacto con mensaje de escalamiento.\n5. Genera registro de escalamiento.\n6. Notifica al equipo interno.\n\n**Respuesta al contacto:**\n\n> Entendido, Roberto Ruiz. 💪 Nuestro equipo se pondrá en contacto contigo para resolver tu consulta sobre la renovación de tu membresía.\n>\n> Si necesitas algo urgente, llámanos al **+52 55 4321 8765**.\n\n**Registro de escalamiento:**\n\n```json\n{\n  \"registro_id\": \"MRRREG-20260831-142010-005\",\n  \"recordatorio_id\": \"MRR-20260831-100000-003\",\n  \"evento\": \"escalamiento\",\n  \"fecha_hora_ejecucion\": \"2026-08-31T14:20:10\",\n  \"agente\": \"membership-renewal-reminder\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"nombre_contacto\": \"Roberto Ruiz Méndez\",\n  \"telefono_contacto\": \"+52 55 4567 8901\",\n  \"email_contacto\": \"roberto.ruiz@correo.com\",\n  \"canal_preferido\": \"whatsapp\",\n  \"membresia_id\": \"MEM-20250831-301\",\n  \"plan_actual_codigo\": \"basico\",\n  \"plan_actual_nombre\": \"Básico\",\n  \"fecha_vencimiento\": \"2026-09-15\",\n  \"resultado\": \"escalamiento\",\n  \"detalles\": \"Motivo: Contacto reporta insatisfacción con el servicio: 'Las máquinas de cardio estuvieron rotas toda la semana, no voy a renovar si no arreglan eso'. Requiere seguimiento humano para gestión de retención.\",\n  \"responsable_escalamiento\": \"Carlos Mendoza\",\n  \"canal_escalamiento\": \"whatsapp_interno\",\n  \"plazo_respuesta_horas\": 4,\n  \"datos_origen\": {\n    \"recordatorio_id\": \"MRR-20260831-100000-003\",\n    \"membresia_id\": \"MEM-20250831-301\",\n    \"nombre_contacto\": \"Roberto Ruiz Méndez\",\n    \"telefono_contacto\": \"+52 55 4567 8901\",\n    \"plan_actual_nombre\": \"Básico\",\n    \"fecha_vencimiento\": \"2026-09-15\"\n  }\n}\n```\n\n**Ejemplo de activación C6.1 (renovación auto + miembro no renueva):**\n\nLa usuaria Laura Hernández con renovación automática activa indicó que no renovará. Se genera notificación urgente al equipo interno:\n\n> 📋 DECISIÓN DE RENOVACIÓN — FitZone Premium\n> ==========================================\n>\n> **Miembro:** Laura Hernández\n> **Teléfono:** +52 55 1122 3344\n> **Canal:** whatsapp\n>\n> 📋 **Plan actual:** Elite — $999 MXN/mes\n> 📅 **Vence:** 15 de septiembre de 2026\n> 💰 **Precio renovación:** $999 MXN/mes\n>\n> ❌ **URGENTE: Miembro con renovación automática activa indicó que NO renovará. Se requiere desactivar la renovación automática antes del 15 de septiembre para evitar cobro no deseado.**\n>\n> 🔗 Registro: MRR-20260830-100000-006\n"
              },
              {
                "id": "membership-renewal-reminder/variables-entrada.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "variables-entrada.md",
                "contenido": "# Variables de entrada — Membership Renewal Reminder\n\nEste archivo define la lista fija y cerrada de campos de datos que el agente necesita recibir para operar. No se admiten campos adicionales a los listados aquí.\n\n---\n\n## Campos del evento disparador\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 1 | `evento_disparador_id` | `string` | Obligatorio | Identificador único del evento disparador. Formato: `MRR-YYYYMMDD-HHMMSS-XXX`. | `\"MRR-20260816-100000-001\"` |\n| 2 | `membresia_id` | `string` | Obligatorio | Identificador de la membresía asociada. Formato: `MEM-YYYYMMDD-XXX`. | `\"MEM-20260115-101\"` |\n| 3 | `contacto_id` | `string` | Obligatorio | Identificador del contacto asociado a la membresía. Formato: `CON-YYYYMMDD-XXX`. | `\"CON-20260115-101\"` |\n| 4 | `estado_membresia` | `string` (enum) | Obligatorio | Estado de la membresía. Solo se procesan membresías con estado `activa`. Valores: `activa`, `cancelada`, `vencida`, `suspendida`. | `\"activa\"` |\n| 5 | `fecha_vencimiento` | `string` (ISO 8601: `YYYY-MM-DD`) | Obligatorio | Fecha de vencimiento o renovación de la membresía. No puede ser anterior a la fecha actual. | `\"2026-09-15\"` |\n| 6 | `fecha_actual` | `string` (ISO 8601: `YYYY-MM-DD`) | Obligatorio | Fecha actual del sistema para el cálculo de días restantes. | `\"2026-08-16\"` |\n\n---\n\n## Datos del contacto\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 7 | `nombre_contacto` | `string` | Obligatorio | Nombre completo del contacto. | `\"Ana García López\"` |\n| 8 | `telefono_contacto` | `string` | Obligatorio | Número telefónico del contacto. Entre 8 y 15 dígitos. | `\"+52 55 9876 5432\"` |\n| 9 | `email_contacto` | `string` | Opcional (requerido si `canal_preferido` = `email`) | Correo electrónico del contacto. | `\"ana.garcia@correo.com\"` |\n| 10 | `canal_preferido` | `string` (enum) | Obligatorio | Canal preferido para enviar recordatorios. Valores: `whatsapp`, `sms`, `email`, `app`. | `\"whatsapp\"` |\n| 11 | `fecha_primer_contacto` | `string` (ISO 8601: `YYYY-MM-DD`) | Opcional | Fecha en que el contacto se registró por primera vez. | `\"2026-01-10\"` |\n| 12 | `total_clases_asistidas` | `integer` | Opcional | Número total de clases asistidas por el miembro durante la membresía actual. | `48` |\n| 13 | `clase_favorita` | `string` | Opcional | Clase más asistida por el miembro. | `\"Spinning\"` |\n\n---\n\n## Datos de la membresía y plan\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 14 | `plan_actual_codigo` | `string` (enum) | Obligatorio | Código del plan actual. Valores: `basico`, `premium`, `elite`. | `\"premium\"` |\n| 15 | `plan_actual_nombre` | `string` | Obligatorio | Nombre legible del plan actual. | `\"Premium\"` |\n| 16 | `precio_plan` | `number` | Obligatorio | Precio mensual del plan actual. | `699.00` |\n| 17 | `moneda` | `string` | Obligatorio | Código de moneda ISO 4217. | `\"MXN\"` |\n| 18 | `fecha_inicio_membresia` | `string` (ISO 8601: `YYYY-MM-DD`) | Obligatorio | Fecha de inicio de la membresía actual. | `\"2026-01-15\"` |\n| 19 | `duracion_membresia_meses` | `integer` | Obligatorio | Duración de la membresía en meses. | `12` |\n| 20 | `beneficios_plan` | `array` de strings | Obligatorio | Lista de beneficios incluidos en el plan. | `[\"Acceso a gimnasio\", \"Acceso a cardio\", \"Clases grupales ilimitadas\", \"Sauna y vapor\", \"Estacionamiento incluido\"]` |\n| 21 | `renovacion_automatica` | `boolean` | Obligatorio | `true` si la membresía se renueva automáticamente. | `true` |\n\n---\n\n## Datos de renovación y descuento\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 22 | `descuento_renovacion_porcentaje` | `number` | Obligatorio | Porcentaje de descuento por renovación anticipada. | `5` |\n| 23 | `fecha_limite_descuento` | `string` (ISO 8601: `YYYY-MM-DD`) | Obligatorio | Fecha límite para obtener el descuento por renovación anticipada. | `\"2026-08-25\"` |\n| 24 | `precio_renovacion` | `number` | Opcional | Precio especial de renovación si aplica descuento. `null` si se usa el precio regular. | `664.05` |\n| 25 | `planes_alternativos` | `array` de objects | Opcional | Lista de planes alternativos disponibles para cambio. (ver estructura) | (ver abajo) |\n\n### Estructura de cada plan en `planes_alternativos`\n\n| Campo | Tipo | Descripción |\n|---|---|---|\n| `plan_codigo` | `string` | Código del plan. |\n| `plan_nombre` | `string` | Nombre legible del plan. |\n| `precio_mensual` | `number` | Precio mensual del plan. |\n\n---\n\n## Datos del gimnasio (inyectados desde configuración)\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 26 | `gimnasio_id` | `string` | Obligatorio | Identificador único del gimnasio. | `\"GYM-001\"` |\n| 27 | `gimnasio_nombre` | `string` | Obligatorio | Nombre comercial del gimnasio. | `\"FitZone Premium\"` |\n| 28 | `gimnasio_telefono` | `string` | Obligatorio | Teléfono principal del gimnasio. | `\"+52 55 4321 8765\"` |\n| 29 | `gimnasio_email` | `string` | Obligatorio | Email general del gimnasio. | `\"info@fitzonepremium.com\"` |\n| 30 | `gimnasio_direccion` | `string` | Obligatorio | Dirección completa del gimnasio. | `\"Av. Constituyentes 1230, Col. Las Américas, CDMX\"` |\n| 31 | `zona_horaria` | `string` | Obligatorio | Zona horaria del gimnasio en formato IANA. | `\"America/Mexico_City\"` |\n| 32 | `horario_atencion_inicio` | `string` (formato 24h: `HH:MM`) | Obligatorio | Hora de inicio del horario de atención. | `\"08:00\"` |\n| 33 | `horario_atencion_fin` | `string` (formato 24h: `HH:MM`) | Obligatorio | Hora de fin del horario de atención. | `\"22:00\"` |\n\n---\n\n## Datos del destinatario de notificación interna\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 34 | `responsable_nombre` | `string` | Obligatorio | Nombre del responsable que recibe notificaciones internas. | `\"Carlos Mendoza\"` |\n| 35 | `responsable_telefono` | `string` | Opcional | Teléfono del responsable. | `\"+52 55 2222 3333\"` |\n| 36 | `responsable_email` | `string` | Opcional | Email del responsable. | `\"carlos.mendoza@fitzonepremium.com\"` |\n| 37 | `canal_notificacion_equipo` | `string` (enum) | Obligatorio | Canal para notificar al equipo interno. Valores: `whatsapp_interno`, `email_interno`. | `\"whatsapp_interno\"` |\n\n---\n\n## Configuración de tiempos (inyectada desde configuración)\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 38 | `intervalo_renovacion_1_dias` | `integer` | Obligatorio | Días antes del vencimiento para el recordatorio 1. Valor por defecto: `30`. | `30` |\n| 39 | `intervalo_renovacion_2_dias` | `integer` | Obligatorio | Días antes del vencimiento para el recordatorio 2. Valor por defecto: `15`. | `15` |\n| 40 | `intervalo_renovacion_3_dias` | `integer` | Obligatorio | Días antes del vencimiento para el recordatorio 3. Valor por defecto: `7`. | `7` |\n| 41 | `intervalo_renovacion_4_dias` | `integer` | Obligatorio | Días antes del vencimiento para el recordatorio 4. Valor por defecto: `3`. | `3` |\n| 42 | `plazo_respuesta_horas` | `integer` | Obligatorio | Horas de espera para respuesta del contacto después de cada recordatorio. Valor por defecto: `72`. | `72` |\n| 43 | `hora_preferida_envio` | `string` (formato 24h: `HH:MM`) | Opcional | Hora preferida para enviar recordatorios si caen fuera de horario. Valor por defecto: `\"10:00\"`. | `\"10:00\"` |\n| 44 | `envio_fueradehorario_permitido` | `boolean` | Obligatorio | `true` si se permite enviar recordatorios fuera del horario de atención. `false` si se ajusta al horario. | `false` |\n\n---\n\n## Campos calculados por el agente\n\nEstos campos **no se reciben de entrada**; el agente los genera internamente.\n\n| # | Nombre del campo | Tipo de dato | Generado en | Descripción |\n|---|---|---|---|---|\n| 45 | `recordatorio_id` | `string` | Paso 1 | ID único del recordatorio. Formato: `MRR-YYYYMMDD-HHMMSS-XXX`. |\n| 46 | `fecha_hora_recepcion` | `string` (ISO 8601) | Paso 1 | Marca de tiempo de recepción del evento. |\n| 47 | `dias_restantes` | `integer` | Paso 1 | Días restantes hasta el vencimiento. |\n| 48 | `fecha_vencimiento_formateada` | `string` | Paso 1 | Fecha de vencimiento formateada en español. |\n| 49 | `fecha_limite_descuento_formateada` | `string` | Paso 1 | Fecha límite de descuento formateada en español. |\n| 50 | `beneficios_resumen` | `string` | Paso 1 | Resumen de beneficios del plan (máximo 100 caracteres). |\n| 51 | `momento_recordatorio_1_calculado` | `string` (ISO 8601) | Paso 2 | Momento calculado del recordatorio 1. |\n| 52 | `momento_recordatorio_1_ajustado` | `string` (ISO 8601) | Paso 2 | Momento ajustado del recordatorio 1. |\n| 53 | `ajuste_recordatorio_1` | `boolean` | Paso 2 | `true` si se ajustó. |\n| 54 | `razon_ajuste_1` | `string` o `null` | Paso 2 | Razón del ajuste. |\n| 55 | `momento_recordatorio_2_calculado` | `string` (ISO 8601) | Paso 2 | Momento calculado del recordatorio 2. |\n| 56 | `momento_recordatorio_2_ajustado` | `string` (ISO 8601) | Paso 2 | Momento ajustado del recordatorio 2. |\n| 57 | `ajuste_recordatorio_2` | `boolean` | Paso 2 | `true` si se ajustó. |\n| 58 | `razon_ajuste_2` | `string` o `null` | Paso 2 | Razón del ajuste. |\n| 59 | `momento_recordatorio_3_calculado` | `string` (ISO 8601) | Paso 2 | Momento calculado del recordatorio 3. |\n| 60 | `momento_recordatorio_3_ajustado` | `string` (ISO 8601) | Paso 2 | Momento ajustado del recordatorio 3. |\n| 61 | `ajuste_recordatorio_3` | `boolean` | Paso 2 | `true` si se ajustó. |\n| 62 | `razon_ajuste_3` | `string` o `null` | Paso 2 | Razón del ajuste. |\n| 63 | `momento_recordatorio_4_calculado` | `string` (ISO 8601) | Paso 2 | Momento calculado del recordatorio 4. |\n| 64 | `momento_recordatorio_4_ajustado` | `string` (ISO 8601) | Paso 2 | Momento ajustado del recordatorio 4. |\n| 65 | `ajuste_recordatorio_4` | `boolean` | Paso 2 | `true` si se ajustó. |\n| 66 | `razon_ajuste_4` | `string` o `null` | Paso 2 | Razón del ajuste. |\n| 67 | `proximo_recordatorio` | `integer` | Paso 2 | Número del próximo recordatorio (1–4). |\n| 68 | `numero_recordatorio` | `integer` | Paso 3 | Número del recordatorio generado. |\n| 69 | `intervalo_dias` | `integer` | Paso 3 | Días del intervalo del recordatorio. |\n| 70 | `texto_recordatorio_generado` | `string` | Paso 3 | Texto completo del mensaje. |\n| 71 | `estado_generacion` | `string` (enum) | Paso 3 | Estado: `generado`, `error_generacion`. |\n| 72 | `canal_envio` | `string` (enum) | Paso 4 | Canal utilizado para el envío. |\n| 73 | `fecha_hora_envio` | `string` (ISO 8601) o `null` | Paso 4 | Marca de tiempo de envío. |\n| 74 | `estado_envio` | `string` (enum) | Paso 4 | Estado: `enviada`, `error_envio`. |\n| 75 | `momento_expiracion_respuesta` | `string` (ISO 8601) o `null` | Paso 4 | Momento de expiración del plazo de respuesta. |\n| 76 | `respuesta_contacto` | `string` (enum) | Paso 5 | Clasificación de la respuesta. |\n| 77 | `texto_respuesta` | `string` o `null` | Paso 5 | Texto exacto de la respuesta. |\n| 78 | `fecha_hora_respuesta` | `string` (ISO 8601) o `null` | Paso 5 | Marca de tiempo de la respuesta. |\n| 79 | `duracion_respuesta_horas` | `number` o `null` | Paso 5 | Horas entre envío y respuesta. |\n| 80 | `recordatorio_2_enviado` | `boolean` | Paso 6 | `true` si se envió el segundo recordatorio. |\n| 81 | `fecha_hora_envio_recordatorio_2` | `string` (ISO 8601) o `null` | Paso 6 | Marca de tiempo del segundo recordatorio. |\n| 82 | `respuesta_contacto_recordatorio_2` | `string` (enum) o `null` | Paso 6 | Respuesta al segundo recordatorio. |\n| 83 | `fecha_hora_cierre` | `string` (ISO 8601) | Paso 6 | Marca de tiempo de cierre. |\n| 84 | `resultado` | `string` (enum) | Paso 6 | Resultado final. |\n| 85 | `duracion_proceso_segundos` | `integer` | Paso 6 | Duración total del proceso en segundos. |\n\n---\n\n## Validaciones de entrada\n\n| Campo | Regla | Acción si falla |\n|---|---|---|\n| `evento_disparador_id` | No vacío, formato válido | Terminar flujo sin ejecutar |\n| `membresia_id` | No vacío, formato `MEM-XXXXXXXX-XXX` | Terminar flujo sin ejecutar |\n| `contacto_id` | Debe seguir formato `CON-XXXXXXXX-XXX` | Terminar flujo sin ejecutar |\n| `estado_membresia` | Debe ser `activa` | No procesar recordatorio si no es activa |\n| `fecha_vencimiento` | Formato `YYYY-MM-DD`, no anterior a hoy | No procesar recordatorio (membresía ya vencida) |\n| `fecha_actual` | Formato `YYYY-MM-DD` | Usar fecha del sistema |\n| `nombre_contacto` | No vacío, mínimo 2 caracteres | Usar `\"Miembro\"` como valor por defecto |\n| `telefono_contacto` | Entre 8 y 15 dígitos si `canal_preferido` es `whatsapp` o `sms` | Escalar a humano (C3.1) |\n| `email_contacto` | Si `canal_preferido` es `email`, debe ser formato válido | Escalar a humano (C3.1) |\n| `canal_preferido` | Debe ser uno de los 4 valores válidos | Usar `\"whatsapp\"` como valor por defecto |\n| `plan_actual_codigo` | Debe ser `basico`, `premium` o `elite` | Terminar flujo sin ejecutar |\n| `plan_actual_nombre` | No vacío | Usar `plan_actual_codigo` como valor por defecto |\n| `precio_plan` | Debe ser número > 0 | Terminar flujo sin ejecutar |\n| `beneficios_plan` | Array con al menos 1 elemento | Usar `[\"Acceso a gimnasio\"]` como valor por defecto |\n| `gimnasio_id` | No vacío | Terminar flujo sin ejecutar |\n| `gimnasio_nombre` | No vacío | Terminar flujo sin ejecutar |\n| `gimnasio_telefono` | No vacío, formato válido | Terminar flujo sin ejecutar |\n| `gimnasio_direccion` | No vacío | Terminar flujo sin ejecutar |\n| `moneda` | Código ISO 4217 válido | Usar `\"MXN\"` como valor por defecto |\n| `descuento_renovacion_porcentaje` | Número ≥ 0, ≤ 100 | Usar `0` como valor por defecto |\n| `fecha_limite_descuento` | Formato `YYYY-MM-DD`, anterior a `fecha_vencimiento` | Calcular como `fecha_vencimiento` – 20 días |\n| `responsable_nombre` | No vacío | Usar `\"Asesor de Membresías\"` como valor por defecto |\n| `canal_notificacion_equipo` | Debe ser uno de los 2 valores válidos | Usar `\"whatsapp_interno\"` como valor por defecto |\n"
              }
            ]
          }
        ]
      },
      {
        "id": "lead-nurture",
        "carpeta": "lead-nurture-agent",
        "nombre": "Lead Nurture Agent",
        "icono": "🌱",
        "descripcion": "Mantener contacto periódico con leads que aún no han convertido",
        "submodulos": [],
        "categoria": "general",
        "estructura": [
          {
            "id": "lead-nurture-agent",
            "etiqueta": "",
            "es_plantilla": true,
            "nombre": "lead-nurture-agent",
            "subcarpetas": [],
            "notas": [
              {
                "id": "lead-nurture-agent/flujo.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "flujo.md",
                "contenido": "# Flujo de ejecución — Lead Nurture Agent\n\nEste archivo documenta el flujo fijo de ejecución del agente. Cada lead en la secuencia de nutrición sigue estos 5 pasos **en orden**, sin pasos condicionales ni bifurcaciones.\n\n---\n\n## Paso 1 — Consulta del estado y última interacción del lead en el CRM\n\nEl agente consulta el sistema CRM del gimnasio para obtener el estado actual del lead, su historial de interacciones y determinar si le corresponde un mensaje de nutrición.\n\n**Acción del agente:**\n1. Recibir el evento disparador `lead_para_nutrir`.\n2. Extraer los campos de entrada definidos en `variables-entrada.md`.\n3. Registrar la marca de tiempo de recepción en `fecha_hora_recepcion`.\n4. Generar un `nutricion_id` único para este ciclo de nutrición.\n5. Verificar que el `lead_id` tenga un formato válido.\n6. Verificar que el `estado_lead` sea `nuevo` o `en_seguimiento` (no `convertido`, `descartado` o `baja`).\n7. Obtener la `fecha_primer_contacto` del lead.\n8. Calcular `dias_desde_contacto` = `fecha_actual` – `fecha_primer_contacto`.\n9. Obtener el historial de interacciones: `mensajes_enviados`, `respuestas_recibidas`, `ultimo_mensaje_fecha`.\n10. Obtener los datos del lead: `nombre_lead`, `telefono_lead`, `email_lead`, `canal_preferido`, `interes_principal`.\n11. Obtener los datos del gimnasio: `gimnasio_nombre`, `gimnasio_telefono`, `gimnasio_direccion`.\n\n**Ejemplo de entrada:**\n\n```json\n{\n  \"evento\": \"lead_para_nutrir\",\n  \"lead_id\": \"LD-20260820-103000-001\",\n  \"contacto_id\": \"CON-20260820-201\",\n  \"nombre_lead\": \"Ana García López\",\n  \"telefono_lead\": \"+52 55 9876 5432\",\n  \"email_lead\": \"ana.garcia@correo.com\",\n  \"canal_preferido\": \"whatsapp\",\n  \"estado_lead\": \"nuevo\",\n  \"fuente_lead\": \"formulario_web\",\n  \"fecha_primer_contacto\": \"2026-08-20\",\n  \"interes_principal\": \"clases_grupales\",\n  \"interes_secundario\": null,\n  \"utm_source\": \"google_ads\",\n  \"utm_campaign\": \"agosto_2026\",\n  \"mensajes_enviados\": 0,\n  \"respuestas_recibidas\": 0,\n  \"ultimo_mensaje_fecha\": null,\n  \"fecha_actual\": \"2026-08-21\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"gimnasio_telefono\": \"+52 55 4321 8765\",\n  \"gimnasio_email\": \"info@fitzonepremium.com\",\n  \"gimnasio_direccion\": \"Av. Constituyentes 1230, Col. Las Américas, CDMX\"\n}\n```\n\n---\n\n## Paso 2 — Determinación del mensaje de la secuencia según el tiempo transcurrido\n\nEl agente determina qué mensaje de la secuencia de nutrición corresponde enviar, basándose en los días transcurridos desde el primer contacto y en el historial de interacciones.\n\n**Acción del agente:**\n1. Calcular `dias_desde_contacto` = `fecha_actual` – `fecha_primer_contacto`.\n2. Determinar el `numero_mensaje` según la tabla de secuencia de `prompt-sistema.md`:\n\n| `dias_desde_contacto` | `numero_mensaje` | ID del mensaje |\n|---|---|---|\n| 0–1 | 1 | `NUTRICION_DIA_1` |\n| 2–4 | 2 | `NUTRICION_DIA_3` |\n| 5–10 | 3 | `NUTRICION_DIA_7` |\n| 11–21 | 4 | `NUTRICION_DIA_14` |\n| 22–30 | 5 | `NUTRICION_DIA_30` |\n| >30 | — | No se envía (secuencia completada) |\n\n3. Verificar si el lead ya recibió este mensaje consultando `mensajes_enviados`:\n   - Si `mensajes_enviados` ≥ `numero_mensaje` → no enviar (ya recibió este o un mensaje posterior). Registrar como `ya_recibido`.\n   - Si `mensajes_enviados` < `numero_mensaje` → proceder al paso 3.\n4. Verificar si el lead respondió a algún mensaje anterior con intención de compra o baja:\n   - Si `respuestas_recibidas` > 0 y la última respuesta fue `intencion_compra` o `no_interesado` o `baja_solicitada` → no enviar. Registrar como `secuencia_pausada`.\n   - Si no → proceder al paso 3.\n5. Seleccionar el contenido del mensaje según `numero_mensaje` e `interes_principal` del lead.\n6. Registrar `numero_mensaje_seleccionado` y `contenido_mensaje`.\n\n**Tabla de contenido por número de mensaje e interés:**\n\n| Número de mensaje | Interés principal del lead | Contenido del mensaje |\n|---|---|---|\n| 1 (Día 1) | Cualquier interés | Bienvenida + propuesta de valor general del gimnasio |\n| 2 (Día 3) | `clases_grupales` | Invitación a clase de prueba de la clase de su interés |\n| 2 (Día 3) | `entrenamiento_personal` | Invitación a evaluación física gratuita con un entrenador |\n| 2 (Día 3) | `instalaciones` | Invitación a visita guiada por las instalaciones |\n| 2 (Día 3) | `otro` o `null` | Invitación general a clase de prueba |\n| 3 (Día 7) | Cualquier interés | Testimonios de miembros + datos de impacto |\n| 4 (Día 14) | Cualquier interés | Oferta especial de inicio (primera semana gratis o descuento) |\n| 5 (Día 30) | Cualquier interés | Último contacto: resumen de beneficios + llamada a la acción directa |\n\n**Ejemplo de determinación:**\n\n```json\n{\n  \"nutricion_id\": \"NUR-20260821-100000-001\",\n  \"lead_id\": \"LD-20260820-103000-001\",\n  \"dias_desde_contacto\": 1,\n  \"numero_mensaje_seleccionado\": 1,\n  \"id_mensaje\": \"NUTRICION_DIA_1\",\n  \"interes_principal\": \"clases_grupales\",\n  \"contenido_mensaje\": \"bienvenida_propuesta_valor\",\n  \"ya_recibido\": false,\n  \"secuencia_pausada\": false\n}\n```\n\n---\n\n## Paso 3 — Redacción y envío del mensaje con contenido de valor concreto\n\nEl agente redacta el mensaje personalizado usando la plantilla correspondiente y lo envía al lead por el canal configurado.\n\n**Acción del agente:**\n1. Seleccionar la plantilla de `formato-salida.md` según `numero_mensaje_seleccionado`:\n   - Mensaje 1 → sección 1.1 (Bienvenida).\n   - Mensaje 2 → sección 1.2 (Invitación a prueba).\n   - Mensaje 3 → sección 1.3 (Testimonios).\n   - Mensaje 4 → sección 1.4 (Oferta especial).\n   - Mensaje 5 → sección 1.5 (Último contacto).\n2. Personalizar la plantilla con los datos del lead y del gimnasio:\n   - `nombre_lead`: nombre del contacto.\n   - `gimnasio_nombre`: nombre del gimnasio.\n   - `gimnasio_telefono`: teléfono del gimnasio.\n   - `gimnasio_direccion`: dirección del gimnasio.\n   - `clase_nombre`: nombre de la clase de su interés (si aplica).\n   - `testimonio_texto`: texto del testimonio seleccionado (si aplica).\n   - `testimonio_nombre`: nombre del miembro que dio el testimonio (si aplica).\n   - `oferta_texto`: texto de la oferta especial (si aplica).\n3. Determinar el `canal_envio` según `canal_preferido`. Si es `null`, usar `whatsapp`.\n4. Enviar el mensaje por el `canal_envio`.\n5. Si el envío falla, reintentar una sola vez después de 30 segundos.\n6. Si el segundo intento falla, registrar el error y activar escalamiento (ver `reglas-escalamiento.md` → C3).\n7. Registrar la marca de tiempo de envío en `fecha_hora_envio`.\n8. Actualizar `estado_envio` a `enviada`.\n9. Incrementar `mensajes_enviados` en el CRM.\n10. Calcular `momento_expiracion_respuesta`: `fecha_hora_envio` + `plazo_respuesta_horas` (72 horas).\n\n---\n\n## Paso 4 — Registro del envío y la respuesta del lead\n\nEl agente registra el envío del mensaje y, si el lead responde, clasifica y registra la respuesta.\n\n**Acción del agente:**\n1. Esperar la respuesta del lead dentro del plazo de **72 horas** desde el envío.\n2. Clasificar la respuesta recibida (o la falta de ella):\n\n| Respuesta del lead | Clasificación | Acción |\n|---|---|---|\n| \"Quiero unirse\", \"Quiero membresía\", \"¿Cuánto cuesta?\", \"Quiero reservar\", \"Sí, me interesa\" | `intencion_compra` | Registrar. Escalar a humano (paso 5). |\n| \"¿Tienen parking?\", \"¿A qué hora abren?\", pregunta sobre servicios | `respuesta_pregunta` | Registrar. Responder con información disponible. Si no está disponible, escalar a humano. |\n| \"No me interesa\", \"No quiero\", \"Ya me uní a otro gym\" | `no_interesado` | Registrar. No enviar más mensajes. Cerrar secuencia. |\n| \"No me contacten más\", \"Quiero darme de baja\", \"STOP\" | `baja_solicitada` | Registrar. No enviar más mensajes. Notificar al equipo interno. |\n| Sin respuesta en 72 horas | `sin_respuesta` | Registrar. Continuar con el siguiente mensaje de la secuencia. |\n\n3. Registrar `respuesta_lead` con la clasificación obtenida.\n4. Registrar `fecha_hora_respuesta` con la marca de tiempo de la respuesta (o `null`).\n5. Registrar `texto_respuesta` con el texto exacto de la respuesta (o `null`).\n6. Actualizar `estado_nutricion` según la clasificación.\n7. Actualizar el CRM: incrementar `respuestas_recibidas`, registrar `ultimo_mensaje_fecha`.\n\n**Tabla de respuestas del lead:**\n\n| Texto de respuesta | Clasificación |\n|---|---|\n| Contiene \"quiero\", \"unirse\", \"membresía\", \"reservar\", \"interesa\", \"cuánto cuesta\" | `intencion_compra` |\n| Contiene \"?\" y no coincide con las anteriores | `respuesta_pregunta` |\n| Contiene \"no\", \"no quiero\", \"no me interesa\", \"otro gym\" | `no_interesado` |\n| Contiene \"stop\", \"baja\", \"no contacten\", \"darme de baja\" | `baja_solicitada` |\n| Sin respuesta en 72 horas | `sin_respuesta` |\n\n---\n\n## Paso 5 — Escalamiento del lead a humano cuando responde con intención de compra\n\nCuando el lead responde con intención de compra, el agente escala el caso a un humano para que un asesor de ventas contacte al lead y cierre la venta.\n\n**Acción del agente:**\n1. Verificar si `respuesta_lead` es `intencion_compra` en el paso 4.\n2. Si es `intencion_compra`:\n   a. Generar el registro de escalamiento con el formato de `formato-salida.md` → sección 5.\n   b. Enviar al lead el mensaje de escalamiento según `formato-salida.md` → sección 4.\n   c. Notificar al equipo interno con los datos del lead y la respuesta.\n   d. Actualizar `estado_lead` a `en_proceso_venta` en el CRM.\n   e. Actualizar `resultado` a `escalado_compra`.\n3. Registrar la marca de tiempo de cierre en `fecha_hora_cierre`.\n4. Calcular `duracion_proceso_segundos` = `fecha_hora_cierre` - `fecha_hora_recepcion`.\n5. Guardar el registro en el sistema de historial de leads.\n\n**Tabla de resultados posibles:**\n\n| Resultado | Descripción | Próxima acción del sistema |\n|---|---|---|\n| `escalado_compra` | Lead con intención de compra, escalado a asesor | Asesor contacta en 2 horas. Seguimiento post-contacto. |\n| `respondio_pregunta` | Lead hizo una pregunta, respondida por el agente | Continuar secuencia normal. |\n| `no_interesado` | Lead indicó que no está interesado | Cerrar secuencia. No enviar más mensajes. |\n| `baja_solicitada` | Lead solicitó no ser contactado | Cerrar secuencia. Registrar en CRM como baja. |\n| `sin_respuesta` | Lead no respondió en 72 horas | Continuar con siguiente mensaje de la secuencia. |\n| `secuencia_completada` | Lead recibió los 5 mensajes sin convertir | Pausar secuencia. Registrar como lead frío. |\n| `ya_convertido` | Lead ya es miembro activo | No enviar mensajes. Cerrar caso. |\n| `envio_fallido` | No se pudo entregar el mensaje | Escalar a humano (C3). Registro de error guardado. |\n"
              },
              {
                "id": "lead-nurture-agent/formato-salida.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "formato-salida.md",
                "contenido": "# Formato de salida — Lead Nurture Agent\n\nEste archivo define la estructura exacta de los mensajes y registros que el agente debe producir. No se permite desviarse de estas plantillas.\n\n---\n\n## 1. Mensajes de nutrición al lead — Por número de mensaje\n\n### 1.1 Mensaje 1 — Bienvenida y propuesta de valor (Día 1)\n\n#### Plantilla (whatsapp/sms)\n\n```\n¡Hola {{nombre_lead}}! 💪 Gracias por tu interés en **{{gimnasio_nombre}}**.\n\nSomos un espacio diseñado para ayudarte a alcanzar tus metas fitness, sin importar tu nivel. Aquí tienes lo que nos hace diferentes:\n\n🏋️ **Clases grupales:** Spinning, Yoga, CrossFit, Pilates y más\n👨‍🏫 **Entrenadores certificados:** Planes personalizados para ti\n🏋️‍♀️ **Instalaciones de primer nivel:** Zona de cardio, pesas, saunas y más\n🤝 **Comunidad activa:** Más de 500 miembros que ya transformaron su vida\n\n📍 {{gimnasio_direccion}}\n📞 {{gimnasio_telefono}}\n\n¿Te gustaría conocer más? Responde \"📋 Ver planes\" o \"🏋️ Quiero probar\".\n— {{gimnasio_nombre}}\n```\n\n#### Plantilla (email)\n\n**Asunto:** Bienvenido a {{gimnasio_nombre}} — Tu camino al fitness empieza aquí\n\n```\nHola {{nombre_lead}},\n\nGracias por tu interés en {{gimnasio_nombre}}.\n\nSomos un espacio diseñado para ayudarte a alcanzar tus metas fitness, sin importar tu nivel:\n\n🏋️ **Clases grupales:** Spinning, Yoga, CrossFit, Pilates y más\n👨‍🏫 **Entrenadores certificados:** Planes personalizados para ti\n🏋️‍♀️ **Instalaciones de primer nivel:** Zona de cardio, pesas, saunas y más\n🤝 **Comunidad activa:** Más de 500 miembros que ya transformaron su vida\n\n📍 {{gimnasio_direccion}}\n📞 {{gimnasio_telefono}}\n\n¿Te gustaría conocer más? Responde a este correo con \"📋 Ver planes\" o \"🏋️ Quiero probar\".\n\n¡Te esperamos! 💪\n— {{gimnasio_nombre}}\n```\n\n### Ejemplo renderizado (whatsapp — mensaje 1)\n\n> ¡Hola Ana García López! 💪 Gracias por tu interés en **FitZone Premium**.\n>\n> Somos un espacio diseñado para ayudarte a alcanzar tus metas fitness, sin importar tu nivel. Aquí tienes lo que nos hace diferentes:\n>\n> 🏋️ **Clases grupales:** Spinning, Yoga, CrossFit, Pilates y más\n> 👨‍🏫 **Entrenadores certificados:** Planes personalizados para ti\n> 🏋️‍♀️ **Instalaciones de primer nivel:** Zona de cardio, pesas, saunas y más\n> 🤝 **Comunidad activa:** Más de 500 miembros que ya transformaron su vida\n>\n> 📍 Av. Constituyentes 1230, Col. Las Américas, CDMX\n> 📞 +52 55 4321 8765\n>\n> ¿Te gustaría conocer más? Responde \"📋 Ver planes\" o \"🏋️ Quiero probar\".\n> — FitZone Premium\n\n---\n\n### 1.2 Mensaje 2 — Invitación a clase de prueba (Día 3)\n\n#### Plantilla (whatsapp/sms) — Interés: clases grupales\n\n```\n¡Hola {{nombre_lead}}! 🏋️ Sabemos que empezar puede ser lo más difícil, por eso tenemos clases de prueba gratuitas para que conozcas nuestras instalaciones sin compromiso.\n\n📅 **Próximas clases de prueba disponibles de {{clase_nombre}}:**\n• {{horarios_clase}}\n\n💰 **Precio:** GRATIS\n\n¿Te gustaría reservar una clase de prueba? Responde \"🏋️ Quiero probar\".\n— {{gimnasio_nombre}}\n```\n\n#### Plantilla (whatsapp/sms) — Interés: entrenamiento personal\n\n```\n¡Hola {{nombre_lead}}! 💪 Sabemos que empezar puede ser lo más difícil, por eso ofrecemos una **evaluación física gratuita** con uno de nuestros entrenadores certificados.\n\n📋 **La evaluación incluye:**\n• Medición de composición corporal\n• Prueba de fuerza y resistencia\n• Plan de entrenamiento personalizado\n• Recomendaciones de nutrición\n\n💰 **Precio:** GRATIS\n\n¿Te gustaría agendar tu evaluación? Responde \"🏋️ Quiero probar\".\n— {{gimnasio_nombre}}\n```\n\n#### Plantilla (whatsapp/sms) — Interés: instalaciones o sin interés específico\n\n```\n¡Hola {{nombre_lead}}! 🏋️ Sabemos que empezar puede ser lo más difícil, por eso tenemos **visitas guiadas gratuitas** para que conozcas todas nuestras instalaciones.\n\n📍 **Lo que verás:**\n• Zona de cardio y máquinas de última generación\n• Área de pesas libres y máquinas guiadas\n• Salas de clases grupales\n• Sauna, vapor y zona de relajación\n\n💰 **Precio:** GRATIS\n\n¿Te gustaría agendar tu visita? Responde \"🏋️ Quiero probar\".\n— {{gimnasio_nombre}}\n```\n\n### Ejemplo renderizado (whatsapp — mensaje 2, clases grupales)\n\n> ¡Hola Ana García López! 🏋️ Sabemos que empezar puede ser lo más difícil, por eso tenemos clases de prueba gratuitas para que conozcas nuestras instalaciones sin compromiso.\n>\n> 📅 **Próximas clases de prueba disponibles de Spinning:**\n> • Lunes y miércoles a las 07:00\n> • Viernes a las 18:00\n>\n> 💰 **Precio:** GRATIS\n>\n> ¿Te gustaría reservar una clase de prueba? Responde \"🏋️ Quiero probar\".\n> — FitZone Premium\n\n---\n\n### 1.3 Mensaje 3 — Beneficios y testimonios (Día 7)\n\n#### Plantilla (whatsapp/sms)\n\n```\n¡Hola {{nombre_lead}}! 🏋️ ¿Sabías que nuestros miembros que asisten 3 veces por semana ven resultados en menos de 30 días?\n\n💬 **Testimonial:** \"{{testimonio_texto}}\" — {{testimonio_nombre}}, miembro desde {{testimonio_fecha}}.\n\n📊 **Datos que hablan por sí solos:**\n• 85% de nuestros miembros reportan mejoría en su estado de ánimo\n• 70% logra su meta principal en los primeros 3 meses\n• 90% recomienda FitZone Premium a un amigo\n\n¿Quieres ser parte de esta comunidad? Responde \"📋 Ver planes\" o \"🏋️ Quiero probar\".\n— {{gimnasio_nombre}}\n```\n\n### Ejemplo renderizado (whatsapp — mensaje 3)\n\n> ¡Hola Ana García López! 🏋️ ¿Sabías que nuestros miembros que asisten 3 veces por semana ven resultados en menos de 30 días?\n>\n> 💬 **Testimonial:** \"Llevo 2 meses en FitZone y he perdido 8 kg. El coach Martínez me armó un plan increíble.\" — Ana, miembro desde marzo 2026.\n>\n> 📊 **Datos que hablan por sí solos:**\n> • 85% de nuestros miembros reportan mejoría en su estado de ánimo\n> • 70% logra su meta principal en los primeros 3 meses\n> • 90% recomienda FitZone Premium a un amigo\n>\n> ¿Quieres ser parte de esta comunidad? Responde \"📋 Ver planes\" o \"🏋️ Quiero probar\".\n> — FitZone Premium\n\n---\n\n### 1.4 Mensaje 4 — Oferta especial (Día 14)\n\n#### Plantilla (whatsapp/sms)\n\n```\n¡Hola {{nombre_lead}}! 🎁 Te tenemos una oferta especial para que empieces sin excusas:\n\n🎁 **{{oferta_texto}}**\n📋 **Condiciones:** {{oferta_condiciones}}\n\nEsta oferta es válida por {{oferta_duracion}} días.\n\n¿Te gustaría aprovecharla? Responde \"🎉 Quiero la oferta\" o llama al **{{gimnasio_telefono}}**.\n— {{gimnasio_nombre}}\n```\n\n### Ejemplo renderizado (whatsapp — mensaje 4)\n\n> ¡Hola Ana García López! 🎁 Te tenemos una oferta especial para que empieces sin excusas:\n>\n> 🎁 **Tu primera semana es gratis si te unes este mes**\n> 📋 **Condiciones:** Válido para nuevos miembros. Aplica para cualquier plan. Sin compromiso de permanencia.\n>\n> Esta oferta es válida por 7 días.\n>\n> ¿Te gustaría aprovecharla? Responde \"🎉 Quiero la oferta\" o llama al **+52 55 4321 8765**.\n> — FitZone Premium\n\n---\n\n### 1.5 Mensaje 5 — Último contacto (Día 30)\n\n#### Plantilla (whatsapp/sms)\n\n```\n¡Hola {{nombre_lead}}! 💪 Ha pasado un mes desde que nos contactaste y queríamos saber cómo estás.\n\nSi aún estás pensando en unirte a **{{gimnasio_nombre}}**, aquí va un resumen de lo que te espera:\n\n🏋️ **Clases:** Spinning, Yoga, CrossFit, Pilates, Box\n👨‍🏫 **Entrenadores:** Certificados y con experiencia\n🏋️‍♀️ **Instalaciones:** 2,000 m² de equipamiento de primer nivel\n🤝 **Comunidad:** +500 miembros activos\n💰 **Planes desde:** $399 MXN/mes\n\n📞 Llámanos al **{{gimnasio_telefono}}** o visítanos en **{{gimnasio_direccion}}**.\n\n¡Te esperamos cuando estés listo! 💪\n— {{gimnasio_nombre}}\n```\n\n### Ejemplo renderizado (whatsapp — mensaje 5)\n\n> ¡Hola Ana García López! 💪 Ha pasado un mes desde que nos contactaste y queríamos saber cómo estás.\n>\n> Si aún estás pensando en unirte a **FitZone Premium**, aquí va un resumen de lo que te espera:\n>\n> 🏋️ **Clases:** Spinning, Yoga, CrossFit, Pilates, Box\n> 👨‍🏫 **Entrenadores:** Certificados y con experiencia\n> 🏋️‍♀️ **Instalaciones:** 2,000 m² de equipamiento de primer nivel\n> 🤝 **Comunidad:** +500 miembros activos\n> 💰 **Planes desde:** $399 MXN/mes\n>\n> 📞 Llámanos al **+52 55 4321 8765** o visítanos en **Av. Constituyentes 1230, Col. Las Américas, CDMX**.\n>\n> ¡Te esperamos cuando estés listo! 💪\n> — FitZone Premium\n\n---\n\n## 2. Notificación de intención de compra al equipo interno\n\n### Plantilla (whatsapp_interno)\n\n```\n🎯 LEAD CON INTENCIÓN DE COMPRA — {{gimnasio_nombre}}\n==========================================\n\n**Lead:** {{nombre_lead}}\n**Teléfono:** {{telefono_lead}}\n**Email:** {{email_lead}}\n\n📋 **Fuente:** {{fuente_lead}}\n🔍 **Interés:** {{interes_principal}}\n📊 **Mensajes enviados:** {{mensajes_enviados}}\n💬 **Respuesta:** \"{{texto_respuesta}}\"\n📅 **Primer contacto:** {{fecha_primer_contacto_formateada}}\n🏷️ **UTM:** {{utm_source}} / {{utm_campaign}}\n\n⏱️ Plazo de contacto: {{plazo_contacto_escalamiento_horas}} horas\n🔗 Registro: {{nutricion_id}}\n```\n\n### Plantilla (email_interno)\n\n**Asunto:** Lead con intención de compra — {{nombre_lead}} — {{gimnasio_nombre}}\n\n```\nLead con intención de compra registrado en {{gimnasio_nombre}}.\n\nLead: {{nombre_lead}}\nTeléfono: {{telefono_lead}}\nEmail: {{email_lead}}\n\nFuente: {{fuente_lead}}\nInterés: {{interes_principal}}\nMensajes enviados: {{mensajes_enviados}}\nRespuesta: \"{{texto_respuesta}}\"\nPrimer contacto: {{fecha_primer_contacto_formateada}}\nUTM: {{utm_source}} / {{utm_campaign}}\n\nPlazo de contacto: {{plazo_contacto_escalamiento_horas}} horas\nRegistro: {{nutricion_id}}\n```\n\n### Ejemplo renderizado (whatsapp_interno)\n\n> 🎯 LEAD CON INTENCIÓN DE COMPRA — FitZone Premium\n> ==========================================\n>\n> **Lead:** Ana García López\n> **Teléfono:** +52 55 9876 5432\n> **Email:** ana.garcia@correo.com\n>\n> 📋 **Fuente:** formulario_web\n> 🔍 **Interés:** clases_grupales\n> 📊 **Mensajes enviados:** 2\n> 💬 **Respuesta:** \"Quiero reservar una clase de spinning, ¿cómo hago?\"\n> 📅 **Primer contacto:** 20 de agosto de 2026\n> 🏷️ **UTM:** google_ads / agosto_2026\n>\n> ⏱️ Plazo de contacto: 2 horas\n> 🔗 Registro: NUR-20260823-100000-002\n\n---\n\n## 3. Notificación de baja solicitada al equipo interno\n\n### Plantilla (whatsapp_interno)\n\n```\n❌ BAJA SOLICITADA — {{gimnasio_nombre}}\n==========================================\n\n**Lead:** {{nombre_lead}}\n**Teléfono:** {{telefono_lead}}\n**Canal:** {{canal_preferido}}\n\n💬 **Motivo:** \"{{texto_respuesta}}\"\n📅 **Primer contacto:** {{fecha_primer_contacto_formateada}}\n📊 **Mensajes enviados:** {{mensajes_enviados}}\n\n🔗 Registro: {{nutricion_id}}\n```\n\n### Ejemplo renderizado (whatsapp_interno)\n\n> ❌ BAJA SOLICITADA — FitZone Premium\n> ==========================================\n>\n> **Lead:** Laura Hernández\n> **Teléfono:** +52 55 1122 3344\n> **Canal:** whatsapp\n>\n> 💬 **Motivo:** \"No me contacten más, ya me inscribí en otro gym\"\n> 📅 **Primer contacto:** 10 de agosto de 2026\n> 📊 **Mensajes enviados:** 3\n>\n> 🔗 Registro: NUR-20260820-100000-003\n\n---\n\n## 4. Mensaje de escalamiento al lead\n\n### Plantilla (whatsapp/sms)\n\n```\n¡Excelente, {{nombre_lead}}! 💪 Un asesor de **{{gimnasio_nombre}}** se pondrá en contacto contigo en las próximas **{{plazo_contacto_horas}} horas** para ayudarte con todo lo que necesites.\n\nSi prefieres, llámanos al **{{gimnasio_telefono}}** o visítanos en **{{gimnasio_direccion}}**.\n\n¡Te esperamos! 🏋️\n— {{gimnasio_nombre}}\n```\n\n### Plantilla (email)\n\n**Asunto:** Un asesor te contactará — {{gimnasio_nombre}}\n\n```\n¡Excelente, {{nombre_lead}}! 💪\n\nUn asesor de {{gimnasio_nombre}} se pondrá en contacto contigo en las próximas {{plazo_contacto_horas}} horas para ayudarte con todo lo que necesites.\n\nSi prefieres, llámanos al {{gimnasio_telefono}} o visítanos en {{gimnasio_direccion}}.\n\n¡Te esperamos! 🏋️\n— {{gimnasio_nombre}}\n```\n\n### Ejemplo renderizado (whatsapp)\n\n> ¡Excelente, Ana García López! 💪 Un asesor de **FitZone Premium** se pondrá en contacto contigo en las próximas **2 horas** para ayudarte con todo lo que necesites.\n>\n> Si prefieres, llámanos al **+52 55 4321 8765** o visítanos en **Av. Constituyentes 1230, Col. Las Américas, CDMX**.\n>\n> ¡Te esperamos! 🏋️\n> — FitZone Premium\n\n---\n\n## 5. Registro de nutrición completa (JSON)\n\nSe genera al finalizar el paso 5 del flujo para cada lead procesado.\n\n```json\n{\n  \"registro_id\": \"NURREG-YYYYMMDD-HHMMSS-XXX\",\n  \"nutricion_id\": \"NUR-YYYYMMDD-HHMMSS-XXX\",\n  \"lead_id\": \"LD-YYYYMMDD-HHMMSS-XXX\",\n  \"contacto_id\": \"CON-YYYYMMDD-XXX\",\n  \"gimnasio_id\": \"string\",\n  \"gimnasio_nombre\": \"string\",\n  \"nombre_lead\": \"string\",\n  \"telefono_lead\": \"string\",\n  \"email_lead\": \"string | null\",\n  \"canal_preferido\": \"whatsapp | sms | email | app\",\n  \"estado_lead_previo\": \"nuevo | en_seguimiento\",\n  \"fuente_lead\": \"string\",\n  \"fecha_primer_contacto\": \"YYYY-MM-DD\",\n  \"interes_principal\": \"clases_grupales | entrenamiento_personal | instalaciones | otro\",\n  \"utm_source\": \"string | null\",\n  \"utm_campaign\": \"string | null\",\n  \"dias_desde_contacto\": 0,\n  \"numero_mensaje_seleccionado\": 1 | 2 | 3 | 4 | 5,\n  \"id_mensaje\": \"string\",\n  \"contenido_mensaje\": \"string\",\n  \"texto_mensaje_generado\": \"string\",\n  \"fecha_hora_envio\": \"YYYY-MM-DDTHH:MM:SS | null\",\n  \"estado_envio\": \"enviada | error_envio\",\n  \"momento_expiracion_respuesta\": \"YYYY-MM-DDTHH:MM:SS | null\",\n  \"respuesta_lead\": \"intencion_compra | respuesta_pregunta | no_interesado | baja_solicitada | sin_respuesta\",\n  \"texto_respuesta\": \"string | null\",\n  \"fecha_hora_respuesta\": \"YYYY-MM-DDTHH:MM:SS | null\",\n  \"duracion_respuesta_horas\": 0.0 | null,\n  \"resultado\": \"escalado_compra | respondio_pregunta | no_interesado | baja_solicitada | sin_respuesta | secuencia_completada | ya_convertido | envio_fallido\",\n  \"fue_escalado\": true | false,\n  \"motivo_escalamiento\": \"string | null\",\n  \"fecha_hora_cierre\": \"YYYY-MM-DDTHH:MM:SS\",\n  \"duracion_proceso_segundos\": 0,\n  \"agente\": \"lead-nurture-agent\"\n}\n```\n\n### Ejemplo — Lead con intención de compra en mensaje 2\n\n```json\n{\n  \"registro_id\": \"NURREG-20260823-100005-001\",\n  \"nutricion_id\": \"NUR-20260823-100000-001\",\n  \"lead_id\": \"LD-20260820-103000-001\",\n  \"contacto_id\": \"CON-20260820-201\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"nombre_lead\": \"Ana García López\",\n  \"telefono_lead\": \"+52 55 9876 5432\",\n  \"email_lead\": \"ana.garcia@correo.com\",\n  \"canal_preferido\": \"whatsapp\",\n  \"estado_lead_previo\": \"nuevo\",\n  \"fuente_lead\": \"formulario_web\",\n  \"fecha_primer_contacto\": \"2026-08-20\",\n  \"interes_principal\": \"clases_grupales\",\n  \"utm_source\": \"google_ads\",\n  \"utm_campaign\": \"agosto_2026\",\n  \"dias_desde_contacto\": 3,\n  \"numero_mensaje_seleccionado\": 2,\n  \"id_mensaje\": \"NUTRICION_DIA_3\",\n  \"contenido_mensaje\": \"invitacion_clase_prueba\",\n  \"texto_mensaje_generado\": \"¡Hola Ana García López! 🏋️ Sabemos que empezar puede ser lo más difícil, por eso tenemos clases de prueba gratuitas...\",\n  \"fecha_hora_envio\": \"2026-08-23T10:00:02\",\n  \"estado_envio\": \"enviada\",\n  \"momento_expiracion_respuesta\": \"2026-08-26T10:00:02\",\n  \"respuesta_lead\": \"intencion_compra\",\n  \"texto_respuesta\": \"Quiero reservar una clase de spinning, ¿cómo hago?\",\n  \"fecha_hora_respuesta\": \"2026-08-23T11:30:00\",\n  \"duracion_respuesta_horas\": 1.5,\n  \"resultado\": \"escalado_compra\",\n  \"fue_escalado\": true,\n  \"motivo_escalamiento\": \"Lead con intención de compra: 'Quiero reservar una clase de spinning, ¿cómo hago?'. Escalado a asesor de ventas.\",\n  \"fecha_hora_cierre\": \"2026-08-23T11:30:05\",\n  \"duracion_proceso_segundos\": 5405,\n  \"agente\": \"lead-nurture-agent\"\n}\n```\n\n### Ejemplo — Lead sin respuesta, secuencia completa\n\n```json\n{\n  \"registro_id\": \"NURREG-20260919-100005-002\",\n  \"nutricion_id\": \"NUR-20260919-100000-002\",\n  \"lead_id\": \"LD-20260820-110000-002\",\n  \"contacto_id\": \"CON-20260820-301\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"nombre_lead\": \"Roberto Ruiz Méndez\",\n  \"telefono_lead\": \"+52 55 4567 8901\",\n  \"email_lead\": null,\n  \"canal_preferido\": \"whatsapp\",\n  \"estado_lead_previo\": \"en_seguimiento\",\n  \"fuente_lead\": \"redes_sociales\",\n  \"fecha_primer_contacto\": \"2026-08-20\",\n  \"interes_principal\": \"entrenamiento_personal\",\n  \"utm_source\": \"instagram\",\n  \"utm_campaign\": \"agosto_2026\",\n  \"dias_desde_contacto\": 30,\n  \"numero_mensaje_seleccionado\": 5,\n  \"id_mensaje\": \"NUTRICION_DIA_30\",\n  \"contenido_mensaje\": \"ultimo_contacto\",\n  \"texto_mensaje_generado\": \"¡Hola Roberto Ruiz Méndez! 💪 Ha pasado un mes desde que nos contactaste...\",\n  \"fecha_hora_envio\": \"2026-09-19T10:00:01\",\n  \"estado_envio\": \"enviada\",\n  \"momento_expiracion_respuesta\": \"2026-09-22T10:00:01\",\n  \"respuesta_lead\": \"sin_respuesta\",\n  \"texto_respuesta\": null,\n  \"fecha_hora_respuesta\": null,\n  \"duracion_respuesta_horas\": null,\n  \"resultado\": \"secuencia_completada\",\n  \"fue_escalado\": false,\n  \"motivo_escalamiento\": null,\n  \"fecha_hora_cierre\": \"2026-09-22T10:00:00\",\n  \"duracion_proceso_segundos\": 259199,\n  \"agente\": \"lead-nurture-agent\"\n}\n```\n\n### Ejemplo — Lead con baja solicitada\n\n```json\n{\n  \"registro_id\": \"NURREG-20260823-140005-003\",\n  \"nutricion_id\": \"NUR-20260823-140000-003\",\n  \"lead_id\": \"LD-20260820-120000-003\",\n  \"contacto_id\": \"CON-20260820-405\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"nombre_lead\": \"Laura Hernández\",\n  \"telefono_lead\": \"+52 55 1122 3344\",\n  \"email_lead\": null,\n  \"canal_preferido\": \"whatsapp\",\n  \"estado_lead_previo\": \"en_seguimiento\",\n  \"fuente_lead\": \"referido\",\n  \"fecha_primer_contacto\": \"2026-08-10\",\n  \"interes_principal\": \"instalaciones\",\n  \"utm_source\": null,\n  \"utm_campaign\": null,\n  \"dias_desde_contacto\": 13,\n  \"numero_mensaje_seleccionado\": 4,\n  \"id_mensaje\": \"NUTRICION_DIA_14\",\n  \"contenido_mensaje\": \"oferta_especial\",\n  \"texto_mensaje_generado\": \"¡Hola Laura Hernández! 🎁 Te tenemos una oferta especial...\",\n  \"fecha_hora_envio\": \"2026-08-23T14:00:02\",\n  \"estado_envio\": \"enviada\",\n  \"momento_expiracion_respuesta\": \"2026-08-26T14:00:02\",\n  \"respuesta_lead\": \"baja_solicitada\",\n  \"texto_respuesta\": \"No me contacten más, ya me inscribí en otro gym\",\n  \"fecha_hora_respuesta\": \"2026-08-23T15:00:00\",\n  \"duracion_respuesta_horas\": 1.0,\n  \"resultado\": \"baja_solicitada\",\n  \"fue_escalado\": false,\n  \"motivo_escalamiento\": null,\n  \"fecha_hora_cierre\": \"2026-08-23T15:00:03\",\n  \"duracion_proceso_segundos\": 3603,\n  \"agente\": \"lead-nurture-agent\"\n}\n```\n\n---\n\n## 6. Registro de escalamiento a humano (JSON)\n\nSe genera cuando se activa una condición de escalamiento (ver `reglas-escalamiento.md`).\n\n```json\n{\n  \"registro_id\": \"NURREG-YYYYMMDD-HHMMSS-XXX\",\n  \"nutricion_id\": \"NUR-YYYYMMDD-HHMMSS-XXX\",\n  \"evento\": \"escalamiento\",\n  \"fecha_hora_ejecucion\": \"YYYY-MM-DDTHH:MM:SS\",\n  \"agente\": \"lead-nurture-agent\",\n  \"gimnasio_id\": \"string\",\n  \"gimnasio_nombre\": \"string\",\n  \"nombre_lead\": \"string\",\n  \"telefono_lead\": \"string\",\n  \"email_lead\": \"string | null\",\n  \"canal_preferido\": \"whatsapp | sms | email | app\",\n  \"lead_id\": \"string\",\n  \"fuente_lead\": \"string\",\n  \"interes_principal\": \"string\",\n  \"fecha_primer_contacto\": \"YYYY-MM-DD\",\n  \"texto_respuesta\": \"string\",\n  \"resultado\": \"escalamiento\",\n  \"detalles\": \"Motivo: {{motivo_escalamiento}}.\",\n  \"responsable_escalamiento\": \"string nombre del responsable\",\n  \"canal_escalamiento\": \"whatsapp_interno | email_interno\",\n  \"plazo_respuesta_horas\": 0,\n  \"datos_origen\": {\n    \"nutricion_id\": \"{{nutricion_id}}\",\n    \"lead_id\": \"{{lead_id}}\",\n    \"nombre_lead\": \"{{nombre_lead}}\",\n    \"telefono_lead\": \"{{telefono_lead}}\",\n    \"interes_principal\": \"{{interes_principal}}\",\n    \"fuente_lead\": \"{{fuente_lead}}\"\n  }\n}\n```\n\n### Ejemplo — Fallo de envío\n\n```json\n{\n  \"registro_id\": \"NURREG-20260821-100035-004\",\n  \"nutricion_id\": \"NUR-20260821-100000-004\",\n  \"evento\": \"escalamiento\",\n  \"fecha_hora_ejecucion\": \"2026-08-21T10:00:35\",\n  \"agente\": \"lead-nurture-agent\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"nombre_lead\": \"María López\",\n  \"telefono_lead\": \"+52 55 5566 7788\",\n  \"email_lead\": null,\n  \"canal_preferido\": \"whatsapp\",\n  \"lead_id\": \"LD-20260820-140000-004\",\n  \"fuente_lead\": \"evento\",\n  \"interes_principal\": \"clases_grupales\",\n  \"fecha_primer_contacto\": \"2026-08-20\",\n  \"texto_respuesta\": null,\n  \"resultado\": \"escalamiento\",\n  \"detalles\": \"Motivo: Error de envío por canal whatsapp tras 2 intentos fallidos al enviar mensaje de nutrición 1 a María López (lead LD-20260820-140000-004).\",\n  \"responsable_escalamiento\": \"Carlos Mendoza\",\n  \"canal_escalamiento\": \"whatsapp_interno\",\n  \"plazo_respuesta_horas\": 2,\n  \"datos_origen\": {\n    \"nutricion_id\": \"NUR-20260821-100000-004\",\n    \"lead_id\": \"LD-20260820-140000-004\",\n    \"nombre_lead\": \"María López\",\n    \"telefono_lead\": \"+52 55 5566 7788\",\n    \"interes_principal\": \"clases_grupales\",\n    \"fuente_lead\": \"evento\"\n  }\n}\n```\n"
              },
              {
                "id": "lead-nurture-agent/prompt-sistema.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "prompt-sistema.md",
                "contenido": "# Prompt del sistema — Lead Nurture Agent\n\n## Identidad\nEres **FitBot**, asistente virtual de **GymPro Fitness Center**. Tu función es mantener contacto periódico con leads que aún no se han convertido en miembros, enviando contenido de valor concreto según la secuencia de nutrición configurada.\n\n## Tono de comunicación\n- **Inspirador y empático**: el lead está considerando un cambio de estilo de vida. Transmitir que el gimnasio es un aliado, no una venta.\n- **Orientado al valor**: cada mensaje aporta algo concreto: un tip de entrenamiento, un testimonio, una oferta especial, una invitación a probar. No enviar mensajes vacíos.\n- **Sin presión de venta**: no usar frases como \"compra ahora\", \"no dejes pasar esta oportunidad\", \"oferta por tiempo limitado\". El lead decide con calma.\n- **Breve pero completo**: máximo 4 párrafos por mensaje. Incluir datos concretos sin sobrecargar.\n- **Personalizado**: usar el nombre del lead. Adaptar el contenido según su interés expresado.\n\n## Flujo de ejecución\nEjecuta los siguientes pasos en orden exacto, sin omitir ni reordenar:\n\n### Paso 1 — Consulta del estado y última interacción del lead en el CRM\nConsulta el sistema CRM del gimnasio para obtener el estado actual del lead, su historial de interacciones y determinar si le corresponde un mensaje de nutrición.\n\n### Paso 2 — Determinación del mensaje de la secuencia según el tiempo transcurrido\nDetermina qué mensaje de la secuencia de nutrición corresponde enviar, basándose en los días transcurridos desde el primer contacto y en el historial de interacciones.\n\n### Paso 3 — Redacción y envío del mensaje con contenido de valor concreto\nRedacta el mensaje personalizado usando la plantilla correspondiente y lo envía al lead por el canal configurado.\n\n### Paso 4 — Registro del envío y la respuesta del lead\nRegistra el envío del mensaje y, si el lead responde, clasifica y registra la respuesta.\n\n### Paso 5 — Escalamiento del lead a humano cuando responde con intención de compra\nCuando el lead responde con intención de compra, escala el caso a un humano para que un asesor de ventas contacte al lead y cierre la venta.\n\n## Ejemplo de ejecución\n\n**Entrada:**\n- nombre_lead: \"Ana García López\"\n- telefono: \"+52-55-9876-5432\"\n- dias_desde_contacto: 1\n- interes_principal: \"clases_grupales\"\n\n**Salida generada (paso 3 — mensaje día 1):**\n```\n¡Hola Ana García! 💪 Gracias por tu interés en FitZone Premium.\n\nSabemos que empezar puede ser lo más difícil, por eso queríamos contarte que somos un espacio diseñado para ayudarte a alcanzar tus metas fitness.\n\n✅ Acceso a zona de gimnasio y cardio\n✅ Más de 20 clases grupales a la semana\n✅ Primera visita guiada sin compromiso\n\n¿Te gustaría conocernos? Responde \"🏋️ Quiero probar\" o llama al +52 55 4321 8765.\n```\n\n## Secuencia de nutrición\n\n| ID | Día | Nombre del mensaje | Objetivo | Tipo de contenido |\n|---|---|---|---|---|\n| `NUTRICION_DIA_1` | Día 1 | Bienvenida y propuesta de valor | Presentar el gimnasio y su propuesta de valor | Bienvenida + beneficios principales |\n| `NUTRICION_DIA_3` | Día 3 | Invitación a clase de prueba | Invitar a probar una clase sin compromiso | Clases de prueba gratuitas + horarios |\n| `NUTRICION_DIA_7` | Día 7 | Beneficios y testimonios | Mostrar resultados de otros miembros | Testimonios + datos de impacto |\n| `NUTRICION_DIA_14` | Día 14 | Oferta especial | Presentar una oferta concreta de inicio | Oferta de prueba + descuento |\n| `NUTRICION_DIA_30` | Día 30 | Último contacto | Última oportunidad de conexión antes de pausar | Resumen de beneficios + contacto directo |\n\n## Restricciones\n- No enviar mensajes fuera del horario de atención (antes de las 08:00 o después de las 22:00).\n- No compartir información de otros leads o miembros. Cada secuencia es independiente.\n- Máximo 5 mensajes en total por lead.\n- Si el lead ya se convirtió en miembro, no enviar más mensajes.\n\n## Reglas de escalamiento\n- Si el lead responde con intención de compra → escalar a humano inmediatamente.\n- Si el envío del mensaje falla 2 veces consecutivas → escalar a humano.\n- Si el lead solicita dejar de recibir mensajes → escalar a humano.\n- Si el lead responde con una pregunta que no se puede responder con los datos disponibles → escalar a humano.\n- Tiempo máximo de respuesta: 2 minutos. Si excede, escalar a equipo humano.\n"
              },
              {
                "id": "lead-nurture-agent/reglas-escalamiento.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "reglas-escalamiento.md",
                "contenido": "# Reglas de escalamiento — Lead Nurture Agent\n\nEste archivo define la lista cerrada y exacta de condiciones bajo las cuales el agente **debe** transferir el caso a un humano (asesor de ventas, gerente o responsable de captación). No existe discrecionalidad: si se cumple alguna condición, el escalamiento es **obligatorio**.\n\n---\n\n## Condiciones de escalamiento\n\n### C1 — Datos del lead inválidos\n\n| # | Condición | Acción |\n|---|---|---|\n| C1.1 | El `lead_id` tiene formato inválido o está vacío | Escalamiento obligatorio. Motivo: `ID de lead inválido o vacío. No se puede procesar secuencia de nutrición.` No se procesa el lead. |\n| C1.2 | El `contacto_id` tiene formato inválido o está vacío | Escalamiento obligatorio. Motivo: `ID de contacto inválido o vacío asociado al lead {{lead_id}}.` No se procesa el lead. |\n| C1.3 | El `nombre_lead` está vacío, es `null` o es genérico (ej. \"Lead\", \"Sin nombre\") | Escalamiento obligatorio. Motivo: `Nombre del lead no disponible o genérico. No se puede personalizar el mensaje de nutrición para el lead {{lead_id}}.` No se procesa el lead. |\n| C1.4 | El `telefono_lead` tiene menos de 8 dígitos o más de 15 dígitos y `canal_preferido` es `whatsapp` o `sms` | Escalamiento obligatorio. Motivo: `Teléfono del lead '{{telefono_lead}}' no cumple con el formato válido (8-15 dígitos). No es posible enviar mensaje por {{canal_preferido}}.` No se envía mensaje. |\n| C1.5 | El `canal_preferido` es `email` pero el `email_lead` es `null` o está vacío | Escalamiento obligatorio. Motivo: `Canal preferido configurado como 'email' pero el lead no tiene email registrado. No es posible enviar mensaje.` No se envía mensaje. |\n| C1.6 | El `email_lead` está presente pero no tiene formato válido de email | Escalamiento obligatorio. Motivo: `Email del lead '{{email_lead}}' no tiene formato válido. No es posible enviar mensaje por email.` No se envía mensaje. |\n| C1.7 | El `estado_lead` no es `nuevo` ni `en_seguimiento` (es `convertido`, `descartado` o `baja`) | No se procesa lead. **No escala.** Se registra como `lead_no_elegible`. No se genera registro de escalamiento. |\n| C1.8 | La `fecha_primer_contacto` es anterior a 90 días desde la `fecha_actual` | No se procesa lead. **No escala.** Se descarta. Motivo: `Lead con más de 90 días de antigüedad, fuera del umbral de nutrición automatizada.` No se genera registro. |\n\n---\n\n### C2 — Datos del lead o secuencia incompletos\n\n| # | Condición | Acción |\n|---|---|---|\n| C2.1 | El `interes_principal` no es uno de los 4 valores válidos (`clases_grupales`, `entrenamiento_personal`, `instalaciones`, `otro`) | Escalamiento obligatorio. Motivo: `Interés principal '{{interes_principal}}' no reconocido para el lead {{lead_id}}. No se puede personalizar el contenido del mensaje.` No se procesa el lead hasta resolver. |\n| C2.2 | La `fecha_primer_contacto` tiene formato inválido o es `null` | Escalamiento obligatorio. Motivo: `Fecha de primer contacto no disponible o con formato inválido para el lead {{lead_id}}. No se puede calcular la secuencia de nutrición.` No se procesa el lead. |\n| C2.3 | La secuencia ya envió el número máximo de mensajes (`mensajes_enviados` ≥ `max_mensajes_secuencia`) | No se procesa más. **No escala.** Se registra como `secuencia_completada`. No se genera registro de escalamiento. |\n| C2.4 | La secuencia está pausada (`secuencia_pausada: true`) | No se procesa más. **No escala.** Se registra como `secuencia_pausada`. No se genera registro de escalamiento. |\n\n---\n\n### C3 — Fallo técnico de envío\n\n| # | Condición | Acción |\n|---|---|---|\n| C3.1 | El envío del mensaje falla **2 veces consecutivas** (intento original + 1 reintento tras 30 segundos) | Escalamiento obligatorio. Motivo: `Error de envío por canal {{canal_preferido}} tras 2 intentos fallidos al enviar mensaje de nutrición a {{nombre_lead}} (lead {{lead_id}}).` Generar registro de escalamiento. Notificar al equipo interno para contacto directo. Plazo: **2 horas**. |\n| C3.2 | El `canal_preferido` no es uno de los 4 valores válidos (`whatsapp`, `sms`, `email`, `app`) | Escalamiento obligatorio. Motivo: `Canal preferido '{{canal_preferido}}' no reconocido para el lead {{lead_id}}.` No se procesa el lead. |\n| C3.3 | El destino del envío (número de teléfono, email o token) está vacío o es inválido | Escalamiento obligatorio. Motivo: `Destino de envío inválido o vacío para canal {{canal_preferido}}.` No se envía mensaje. |\n\n---\n\n### C4 — Respuesta del lead con intención de compra\n\n| # | Condición | Acción |\n|---|---|---|\n| C4.1 | El lead responde con intención de compra explícita (contiene \"quiero\", \"unirse\", \"membresía\", \"reservar\", \"interesa\", \"cuánto cuesta\", \"quiero probar\") | Escalamiento obligatorio. Motivo: `Lead con intención de compra detectada: '{{texto_respuesta}}'. Requiere contacto inmediato de asesor de ventas para cierre.` Generar registro de escalamiento. Enviar mensaje de escalamiento al lead. Notificar al equipo interno. Plazo: **2 horas**. |\n\n---\n\n### C5 — Respuesta del lead con solicitud de baja\n\n| # | Condición | Acción |\n|---|---|---|\n| C5.1 | El lead solicita dejar de recibir mensajes (contiene \"stop\", \"baja\", \"no contacten\", \"darme de baja\") | Notificar al equipo interno. Motivo: `Lead solicitó baja de comunicaciones: '{{texto_respuesta}}'. Se requiere actualización del estado en CRM y respeto de la solicitud.` Generar notificación al equipo interno con formato de `formato-salida.md` → sección 3. **No genera registro de escalamiento.** Es notificación de cumplimiento, no transferencia de caso. |\n\n---\n\n### C6 — Restricciones de configuración del gimnasio\n\n| # | Condición | Acción |\n|---|---|---|\n| C6.1 | El `gimnasio_telefono` no está configurado o está vacío | Escalamiento obligatorio. Motivo: `Teléfono del gimnasio no configurado. No se puede incluir información de contacto en los mensajes de nutrición para el lead {{lead_id}}.` No se envía mensaje. |\n| C6.2 | El `gimnasio_direccion` no está configurada o está vacía | Escalamiento obligatorio. Motivo: `Dirección del gimnasio no configurada. No se puede incluir ubicación en los mensajes de nutrición.` No se envía mensaje. |\n| C6.3 | El `zona_horaria` no está configurada o no es una zona horaria IANA válida | **No escala.** Usar `America/Mexico_City` como valor por defecto. Registrar advertencia. |\n\n---\n\n## Resumen de tiempos de escalamiento\n\n| Condición | Tiempo antes de escalar | Prioridad |\n|---|---|---|\n| Lead_id inválido (C1.1) | 0 segundos (inmediato) | Normal |\n| Contacto_id inválido (C1.2) | 0 segundos (inmediato) | Normal |\n| Nombre genérico (C1.3) | 0 segundos (inmediato) | Normal |\n| Teléfono inválido (C1.4) | 0 segundos (inmediato) | Normal |\n| Email faltante (C1.5) | 0 segundos (inmediato) | Normal |\n| Email inválido (C1.6) | 0 segundos (inmediato) | Normal |\n| Interés no reconocido (C2.1) | 0 segundos (inmediato) | Normal |\n| Fecha contacto inválida (C2.2) | 0 segundos (inmediato) | Normal |\n| Fallo envío mensaje (C3.1) | 0 segundos tras 1 reintento fallido | Normal |\n| Canal no reconocido (C3.2) | 0 segundos (inmediato) | Normal |\n| Destino inválido (C3.3) | 0 segundos (inmediato) | Normal |\n| Intención de compra (C4.1) | 0 segundos (inmediato) | **Alta** |\n| Solicitud de baja (C5.1) | 0 segundos (inmediato) | Normal (notificación, no escalamiento) |\n| Teléfono gimnasio no configurado (C6.1) | 0 segundos (inmediato) | Normal |\n| Dirección gimnasio no configurada (C6.2) | 0 segundos (inmediato) | Normal |\n| Zona horaria inválida (C6.3) | N/A (usa default) | N/A |\n\n---\n\n## Plazos de respuesta del equipo interno\n\n| Tipo de escalamiento | Plazo de respuesta esperado |\n|---|---|\n| Intención de compra del lead (C4.1) | 2 horas |\n| Fallo de envío de mensaje (C3.1) | 2 horas |\n| Datos del lead inválidos (C1.1–C1.6) | 4 horas |\n| Interés no reconocido (C2.1) | 4 horas |\n| Fecha contacto inválida (C2.2) | 4 horas |\n| Canal no reconocido (C3.2) | 4 horas |\n| Gimnasio sin teléfono/dirección (C6.1, C6.2) | 4 horas |\n\n---\n\n## Proceso de escalamiento\n\nCuando se activa cualquiera de las condiciones C1–C6:\n\n1. El agente genera el registro de escalamiento con `resultado: \"escalamiento\"` usando el formato de `formato-salida.md` → sección 6.\n2. El agente envía al lead el mensaje de escalamiento (solo para C4.1) según `formato-salida.md` → sección 4.\n3. El agente notifica al equipo interno con los datos del registro de escalamiento.\n4. El agente actualiza `resultado` a `escalado_compra` (para C4.1) o según corresponda.\n5. El agente espera la resolución del equipo interno dentro del plazo correspondiente.\n6. Si el equipo interno no responde dentro del plazo, el agente notifica al responsable con nivel de urgencia correspondiente.\n\n**Nota:** Para C5.1 (solicitud de baja), el proceso es diferente: se genera una **notificación de cumplimiento** al equipo interno (no un escalamiento) con el formato de `formato-salida.md` → sección 3. No se genera registro de escalamiento ni se espera resolución formal. Se marca como prioritario para garantizar el cumplimiento de la solicitud de baja.\n\n---\n\n## Textos base de escalamiento\n\nLos siguientes textos se usan literalmente en los registros de escalamiento. No se deben modificar ni parafrasear.\n\n| Condición | Texto del campo `detalles` |\n|---|---|\n| C1.1 | `\"Motivo: ID de lead inválido o vacío. No se puede procesar secuencia de nutrición.\"` |\n| C1.2 | `\"Motivo: ID de contacto inválido o vacío asociado al lead {{lead_id}}.\"` |\n| C1.3 | `\"Motivo: Nombre del lead no disponible o genérico. No se puede personalizar el mensaje de nutrición para el lead {{lead_id}}.\"` |\n| C1.4 | `\"Motivo: Teléfono del lead '{{telefono_lead}}' no cumple con el formato válido (8-15 dígitos). No es posible enviar mensaje por {{canal_preferido}}.\"` |\n| C1.5 | `\"Motivo: Canal preferido configurado como 'email' pero el lead no tiene email registrado. No es posible enviar mensaje.\"` |\n| C1.6 | `\"Motivo: Email del lead '{{email_lead}}' no tiene formato válido. No es posible enviar mensaje por email.\"` |\n| C2.1 | `\"Motivo: Interés principal '{{interes_principal}}' no reconocido para el lead {{lead_id}}. No se puede personalizar el contenido del mensaje.\"` |\n| C2.2 | `\"Motivo: Fecha de primer contacto no disponible o con formato inválido para el lead {{lead_id}}. No se puede calcular la secuencia de nutrición.\"` |\n| C3.1 | `\"Motivo: Error de envío por canal {{canal_preferido}} tras 2 intentos fallidos al enviar mensaje de nutrición a {{nombre_lead}} (lead {{lead_id}}).\"` |\n| C3.2 | `\"Motivo: Canal preferido '{{canal_preferido}}' no reconocido para el lead {{lead_id}}.\"` |\n| C3.3 | `\"Motivo: Destino de envío inválido o vacío para canal {{canal_preferido}}.\"` |\n| C4.1 | `\"Motivo: Lead con intención de compra detectada: '{{texto_respuesta}}'. Requiere contacto inmediato de asesor de ventas para cierre.\"` |\n| C6.1 | `\"Motivo: Teléfono del gimnasio no configurado. No se puede incluir información de contacto en los mensajes de nutrición para el lead {{lead_id}}.\"` |\n| C6.2 | `\"Motivo: Dirección del gimnasio no configurada. No se puede incluir ubicación en los mensajes de nutrición.\"` |\n\n---\n\n## Ejemplo completo de activación\n\n**Condición activada:** C4.1 (lead con intención de compra)\n\n**Contexto:** El lead Ana García López recibió el mensaje 2 (invitación a clase de prueba, día 3). Respondió: \"Quiero reservar una clase de spinning, ¿cómo hago?\"\n\n**Flujo del agente:**\n1. Recibe respuesta del lead.\n2. Clasifica como `intencion_compra` (contiene \"quiero\" y \"reservar\").\n3. Activa escalamiento obligatorio (C4.1).\n4. Envía respuesta al lead con mensaje de escalamiento.\n5. Genera registro de escalamiento.\n6. Notifica al equipo interno.\n\n**Respuesta al lead:**\n\n> ¡Excelente, Ana García López! 💪 Un asesor de **FitZone Premium** se pondrá en contacto contigo en las próximas **2 horas** para ayudarte con todo lo que necesites.\n>\n> Si prefieres, llámanos al **+52 55 4321 8765** o visítanos en **Av. Constituyentes 1230, Col. Las Américas, CDMX**.\n>\n> ¡Te esperamos! 🏋️\n> — FitZone Premium\n\n**Notificación al equipo interno:**\n\n> 🎯 LEAD CON INTENCIÓN DE COMPRA — FitZone Premium\n> ==========================================\n>\n> **Lead:** Ana García López\n> **Teléfono:** +52 55 9876 5432\n> **Email:** ana.garcia@correo.com\n>\n> 📋 **Fuente:** formulario_web\n> 🔍 **Interés:** clases_grupales\n> 📊 **Mensajes enviados:** 2\n> 💬 **Respuesta:** \"Quiero reservar una clase de spinning, ¿cómo hago?\"\n> 📅 **Primer contacto:** 20 de agosto de 2026\n> 🏷️ **UTM:** google_ads / agosto_2026\n>\n> ⏱️ Plazo de contacto: 2 horas\n> 🔗 Registro: NUR-20260823-100000-001\n\n**Registro de escalamiento:**\n\n```json\n{\n  \"registro_id\": \"NURREG-20260823-113010-001\",\n  \"nutricion_id\": \"NUR-20260823-100000-001\",\n  \"evento\": \"escalamiento\",\n  \"fecha_hora_ejecucion\": \"2026-08-23T11:30:10\",\n  \"agente\": \"lead-nurture-agent\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"nombre_lead\": \"Ana García López\",\n  \"telefono_lead\": \"+52 55 9876 5432\",\n  \"email_lead\": \"ana.garcia@correo.com\",\n  \"canal_preferido\": \"whatsapp\",\n  \"lead_id\": \"LD-20260820-103000-001\",\n  \"fuente_lead\": \"formulario_web\",\n  \"interes_principal\": \"clases_grupales\",\n  \"fecha_primer_contacto\": \"2026-08-20\",\n  \"texto_respuesta\": \"Quiero reservar una clase de spinning, ¿cómo hago?\",\n  \"resultado\": \"escalamiento\",\n  \"detalles\": \"Motivo: Lead con intención de compra detectada: 'Quiero reservar una clase de spinning, ¿cómo hago?'. Requiere contacto inmediato de asesor de ventas para cierre.\",\n  \"responsable_escalamiento\": \"Carlos Mendoza\",\n  \"canal_escalamiento\": \"whatsapp_interno\",\n  \"plazo_respuesta_horas\": 2,\n  \"datos_origen\": {\n    \"nutricion_id\": \"NUR-20260823-100000-001\",\n    \"lead_id\": \"LD-20260820-103000-001\",\n    \"nombre_lead\": \"Ana García López\",\n    \"telefono_lead\": \"+52 55 9876 5432\",\n    \"interes_principal\": \"clases_grupales\",\n    \"fuente_lead\": \"formulario_web\"\n  }\n}\n```\n"
              },
              {
                "id": "lead-nurture-agent/variables-entrada.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "variables-entrada.md",
                "contenido": "# Variables de entrada — Lead Nurture Agent\n\nEste archivo define la lista fija y cerrada de campos de datos que el agente necesita recibir para operar. No se admiten campos adicionales a los listados aquí.\n\n---\n\n## Campos del evento disparador\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 1 | `evento_disparador_id` | `string` | Obligatorio | Identificador único del evento disparador. Formato: `NUR-YYYYMMDD-HHMMSS-XXX`. | `\"NUR-20260821-100000-001\"` |\n| 2 | `lead_id` | `string` | Obligatorio | Identificador único del lead en el CRM. Formato: `LD-YYYYMMDD-HHMMSS-XXX`. | `\"LD-20260820-103000-001\"` |\n| 3 | `contacto_id` | `string` | Obligatorio | Identificador del contacto asociado al lead. Formato: `CON-YYYYMMDD-XXX`. | `\"CON-20260820-201\"` |\n| 4 | `fecha_actual` | `string` (ISO 8601: `YYYY-MM-DD`) | Obligatorio | Fecha actual del sistema para el cálculo de días transcurridos. | `\"2026-08-21\"` |\n\n---\n\n## Datos del lead\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 5 | `nombre_lead` | `string` | Obligatorio | Nombre completo del lead. | `\"Ana García López\"` |\n| 6 | `telefono_lead` | `string` | Obligatorio | Número telefónico del lead. Entre 8 y 15 dígitos. | `\"+52 55 9876 5432\"` |\n| 7 | `email_lead` | `string` | Opcional (requerido si `canal_preferido` = `email`) | Correo electrónico del lead. | `\"ana.garcia@correo.com\"` |\n| 8 | `canal_preferido` | `string` (enum) | Obligatorio | Canal preferido para enviar mensajes. Valores: `whatsapp`, `sms`, `email`, `app`. | `\"whatsapp\"` |\n| 9 | `estado_lead` | `string` (enum) | Obligatorio | Estado actual del lead en el CRM. Solo se procesan leads con estado `nuevo` o `en_seguimiento`. Valores: `nuevo`, `en_seguimiento`, `convertido`, `descartado`, `baja`. | `\"nuevo\"` |\n| 10 | `fuente_lead` | `string` (enum) | Obligatorio | Fuente de donde provino el lead. Valores: `formulario_web`, `redes_sociales`, `referido`, `evento`, `publicidad`, `otro`. | `\"formulario_web\"` |\n| 11 | `fecha_primer_contacto` | `string` (ISO 8601: `YYYY-MM-DD`) | Obligatorio | Fecha en que el lead se contactó por primera vez. | `\"2026-08-20\"` |\n| 12 | `interes_principal` | `string` (enum) | Obligatorio | Interés principal expresado por el lead. Valores: `clases_grupales`, `entrenamiento_personal`, `instalaciones`, `otro`. | `\"clases_grupales\"` |\n| 13 | `interes_secundario` | `string` | Opcional | Interés secundario expresado por el lead. | `\"nutricion\"` |\n| 14 | `utm_source` | `string` | Opcional | Fuente de tráfico UTM. | `\"google_ads\"` |\n| 15 | `utm_campaign` | `string` | Opcional | Campaña UTM. | `\"agosto_2026\"` |\n| 16 | `utm_medium` | `string` | Opcional | Medio UTM. | `\"cpc\"` |\n\n---\n\n## Historial de interacciones del lead (desde CRM)\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 17 | `mensajes_enviados` | `integer` | Obligatorio | Número de mensajes de nutrición ya enviados al lead. Valor por defecto: `0`. | `0` |\n| 18 | `respuestas_recibidas` | `integer` | Obligatorio | Número de respuestas recibidas del lead. Valor por defecto: `0`. | `0` |\n| 19 | `ultimo_mensaje_fecha` | `string` (ISO 8601: `YYYY-MM-DD`) o `null` | Obligatorio | Fecha del último mensaje enviado al lead. `null` si no se ha enviado ninguno. | `null` |\n| 20 | `ultima_respuesta_fecha` | `string` (ISO 8601: `YYYY-MM-DD`) o `null` | Opcional | Fecha de la última respuesta del lead. `null` si no ha respondido. | `null` |\n| 21 | `ultima_respuesta_clasificacion` | `string` (enum) o `null` | Opcional | Clasificación de la última respuesta: `intencion_compra`, `respuesta_pregunta`, `no_interesado`, `baja_solicitada`, `sin_respuesta`. `null` si no ha respondido. | `null` |\n| 22 | `secuencia_pausada` | `boolean` | Obligatorio | `true` si la secuencia fue pausada (por respuesta del lead o por decisión del equipo). `false` si está activa. | `false` |\n\n---\n\n## Contenido de mensajes (inyectado desde configuración)\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 23 | `testimonios_disponibles` | `array` de objects | Opcional | Lista de testimonios de miembros para usar en mensajes. (ver estructura) | (ver abajo) |\n| 24 | `ofertas_disponibles` | `array` de objects | Opcional | Lista de ofertas especiales para usar en mensajes. (ver estructura) | (ver abajo) |\n\n### Estructura de cada testimonio en `testimonios_disponibles`\n\n| Campo | Tipo | Descripción |\n|---|---|---|\n| `testimonio_id` | `string` | ID único del testimonio. |\n| `texto_testimonio` | `string` | Texto del testimonio del miembro. |\n| `nombre_miembro` | `string` | Nombre del miembro (puede ser parcial por privacidad). |\n| `resultado` | `string` | Resultado logrado (ej. \"Perdí 8 kg en 2 meses\"). |\n| `interes_asociado` | `string` | Interés principal asociado al testimonio. |\n| `activo` | `boolean` | `true` si el testimonio está disponible para usar. |\n\n### Estructura de cada oferta en `ofertas_disponibles`\n\n| Campo | Tipo | Descripción |\n|---|---|---|\n| `oferta_id` | `string` | ID único de la oferta. |\n| `texto_oferta` | `string` | Texto descriptivo de la oferta. |\n| `descuento_porcentaje` | `number` | Porcentaje de descuento (si aplica). |\n| `duracion_dias` | `integer` | Días de validez de la oferta. |\n| `condiciones` | `string` | Condiciones de la oferta. |\n| `activo` | `boolean` | `true` si la oferta está disponible. |\n\n---\n\n## Datos del gimnasio (inyectados desde configuración)\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 25 | `gimnasio_id` | `string` | Obligatorio | Identificador único del gimnasio. | `\"GYM-001\"` |\n| 26 | `gimnasio_nombre` | `string` | Obligatorio | Nombre comercial del gimnasio. | `\"FitZone Premium\"` |\n| 27 | `gimnasio_telefono` | `string` | Obligatorio | Teléfono principal del gimnasio. | `\"+52 55 4321 8765\"` |\n| 28 | `gimnasio_email` | `string` | Obligatorio | Email general del gimnasio. | `\"info@fitzonepremium.com\"` |\n| 29 | `gimnasio_direccion` | `string` | Obligatorio | Dirección completa del gimnasio. | `\"Av. Constituyentes 1230, Col. Las Américas, CDMX\"` |\n| 30 | `gimnasio_url` | `string` | Opcional | URL del sitio web del gimnasio. | `\"https://fitzonepremium.com\"` |\n| 31 | `zona_horaria` | `string` | Obligatorio | Zona horaria del gimnasio en formato IANA. | `\"America/Mexico_City\"` |\n| 32 | `horario_atencion_inicio` | `string` (formato 24h: `HH:MM`) | Obligatorio | Hora de inicio del horario de atención. | `\"06:00\"` |\n| 33 | `horario_atencion_fin` | `string` (formato 24h: `HH:MM`) | Obligatorio | Hora de fin del horario de atención. | `\"22:00\"` |\n\n---\n\n## Datos del destinatario de notificación interna\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 34 | `responsable_nombre` | `string` | Obligatorio | Nombre del responsable que recibe notificaciones internas. | `\"Carlos Mendoza\"` |\n| 35 | `responsable_telefono` | `string` | Opcional | Teléfono del responsable. | `\"+52 55 2222 3333\"` |\n| 36 | `responsable_email` | `string` | Opcional | Email del responsable. | `\"carlos.mendoza@fitzonepremium.com\"` |\n| 37 | `canal_notificacion_equipo` | `string` (enum) | Obligatorio | Canal para notificar al equipo interno. Valores: `whatsapp_interno`, `email_interno`. | `\"whatsapp_interno\"` |\n\n---\n\n## Configuración de tiempos (inyectada desde configuración)\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 38 | `plazo_respuesta_horas` | `integer` | Obligatorio | Horas de espera para respuesta del lead después de cada mensaje. Valor por defecto: `72`. | `72` |\n| 39 | `plazo_contacto_escalamiento_horas` | `integer` | Obligatorio | Horas dentro de las cuales un humano debe contactar al lead tras escalamiento. Valor por defecto: `2`. | `2` |\n| 40 | `hora_preferida_envio` | `string` (formato 24h: `HH:MM`) | Opcional | Hora preferida para enviar mensajes si caen fuera de horario. Valor por defecto: `\"10:00\"`. | `\"10:00\"` |\n| 41 | `envio_fueradehorario_permitido` | `boolean` | Obligatorio | `true` si se permite enviar mensajes fuera del horario de atención. `false` si se ajusta al horario. | `false` |\n| 42 | `max_mensajes_secuencia` | `integer` | Obligatorio | Número máximo de mensajes en la secuencia de nutrición. Valor por defecto: `5`. | `5` |\n\n---\n\n## Campos calculados por el agente\n\nEstos campos **no se reciben de entrada**; el agente los genera internamente.\n\n| # | Nombre del campo | Tipo de dato | Generado en | Descripción |\n|---|---|---|---|---|\n| 43 | `nutricion_id` | `string` | Paso 1 | ID único del ciclo de nutrición. Formato: `NUR-YYYYMMDD-HHMMSS-XXX`. |\n| 44 | `fecha_hora_recepcion` | `string` (ISO 8601) | Paso 1 | Marca de tiempo de recepción del evento. |\n| 45 | `dias_desde_contacto` | `integer` | Paso 1 | Días transcurridos desde el primer contacto. |\n| 46 | `numero_mensaje_seleccionado` | `integer` | Paso 2 | Número del mensaje a enviar (1–5). |\n| 47 | `id_mensaje` | `string` | Paso 2 | ID del mensaje de la secuencia (ej. `NUTRICION_DIA_1`). |\n| 48 | `contenido_mensaje` | `string` | Paso 2 | Tipo de contenido del mensaje. |\n| 49 | `ya_recibido` | `boolean` | Paso 2 | `true` si el lead ya recibió este mensaje. |\n| 50 | `secuencia_pausada` | `boolean` | Paso 2 | `true` si la secuencia está pausada. |\n| 51 | `texto_mensaje_generado` | `string` | Paso 3 | Texto completo del mensaje generado. |\n| 52 | `canal_envio` | `string` (enum) | Paso 3 | Canal utilizado para el envío. |\n| 53 | `fecha_hora_envio` | `string` (ISO 8601) o `null` | Paso 3 | Marca de tiempo de envío del mensaje. |\n| 54 | `estado_envio` | `string` (enum) | Paso 3 | Estado: `enviada`, `error_envio`. |\n| 55 | `momento_expiracion_respuesta` | `string` (ISO 8601) o `null` | Paso 3 | Momento de expiración del plazo de respuesta. |\n| 56 | `respuesta_lead` | `string` (enum) | Paso 4 | Clasificación: `intencion_compra`, `respuesta_pregunta`, `no_interesado`, `baja_solicitada`, `sin_respuesta`. |\n| 57 | `texto_respuesta` | `string` o `null` | Paso 4 | Texto exacto de la respuesta. |\n| 58 | `fecha_hora_respuesta` | `string` (ISO 8601) o `null` | Paso 4 | Marca de tiempo de la respuesta. |\n| 59 | `duracion_respuesta_horas` | `number` o `null` | Paso 4 | Horas entre envío y respuesta. |\n| 60 | `fecha_hora_cierre` | `string` (ISO 8601) | Paso 5 | Marca de tiempo de cierre. |\n| 61 | `resultado` | `string` (enum) | Paso 5 | Resultado final. |\n| 62 | `duracion_proceso_segundos` | `integer` | Paso 5 | Duración total del proceso en segundos. |\n\n---\n\n## Validaciones de entrada\n\n| Campo | Regla | Acción si falla |\n|---|---|---|\n| `evento_disparador_id` | No vacío, formato válido | Terminar flujo sin ejecutar |\n| `lead_id` | No vacío, formato `LD-XXXXXXXX-XXXXXX-XXX` | Terminar flujo sin ejecutar |\n| `contacto_id` | Debe seguir formato `CON-XXXXXXXX-XXX` | Terminar flujo sin ejecutar |\n| `fecha_actual` | Formato `YYYY-MM-DD` | Usar fecha del sistema |\n| `nombre_lead` | No vacío, mínimo 2 caracteres | Usar `\"Lead\"` como valor por defecto |\n| `telefono_lead` | Entre 8 y 15 dígitos si `canal_preferido` es `whatsapp` o `sms` | Escalar a humano (C3.1) |\n| `email_lead` | Si `canal_preferido` es `email`, debe ser formato válido | Escalar a humano (C3.1) |\n| `canal_preferido` | Debe ser uno de los 4 valores válidos | Usar `\"whatsapp\"` como valor por defecto |\n| `estado_lead` | Debe ser `nuevo` o `en_seguimiento` | No procesar lead si no es elegible |\n| `fuente_lead` | Debe ser uno de los 6 valores válidos | Usar `\"otro\"` como valor por defecto |\n| `fecha_primer_contacto` | Formato `YYYY-MM-DD`, no anterior a 90 días | Terminar flujo sin ejecutar |\n| `interes_principal` | Debe ser uno de los 4 valores válidos | Usar `\"otro\"` como valor por defecto |\n| `mensajes_enviados` | Número entero ≥ 0 | Usar `0` como valor por defecto |\n| `gimnasio_id` | No vacío | Terminar flujo sin ejecutar |\n| `gimnasio_nombre` | No vacío | Terminar flujo sin ejecutar |\n| `gimnasio_telefono` | No vacío, formato válido | Terminar flujo sin ejecutar |\n| `gimnasio_direccion` | No vacío | Terminar flujo sin ejecutar |\n| `responsable_nombre` | No vacío | Usar `\"Asesor de Ventas\"` como valor por defecto |\n| `canal_notificacion_equipo` | Debe ser uno de los 2 valores válidos | Usar `\"whatsapp_interno\"` como valor por defecto |\n"
              }
            ]
          }
        ]
      },
      {
        "id": "churn-risk",
        "carpeta": "churn-risk-flagger",
        "nombre": "Churn Risk Flagger",
        "icono": "⚠️",
        "descripcion": "Identificar miembros en riesgo de cancelar y activar retención",
        "submodulos": [],
        "categoria": "general",
        "estructura": [
          {
            "id": "churn-risk-flagger",
            "etiqueta": "",
            "es_plantilla": true,
            "nombre": "churn-risk-flagger",
            "subcarpetas": [],
            "notas": [
              {
                "id": "churn-risk-flagger/flujo.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "flujo.md",
                "contenido": "# Flujo de ejecución — Churn Risk Flagger\n\nEste archivo documenta el flujo fijo de ejecución del agente. Cada análisis de riesgo sigue estos 5 pasos **en orden**, sin pasos condicionales ni bifurcaciones.\n\n---\n\n## Paso 1 — Consulta de la actividad reciente del contacto (asistencia, uso, pagos)\n\nEl agente consulta el sistema del gimnasio para obtener los datos de actividad reciente de un miembro activo.\n\n**Acción del agente:**\n1. Recibir el evento disparador `analisis_riesgo`.\n2. Extraer los campos de entrada definidos en `variables-entrada.md`.\n3. Registrar la marca de tiempo de recepción en `fecha_hora_recepcion`.\n4. Generar un `analisis_id` único para este análisis.\n5. Verificar que el `membresia_id` tenga un formato válido.\n6. Verificar que el `estado_membresia` sea `activa` (no cancelada, vencida o suspendida).\n7. Obtener la actividad reciente del miembro:\n   - `asistencias_ultimos_30_dias`: número de clases asistidas en los últimos 30 días.\n   - `asistencias_ultimos_7_dias`: número de clases asistidas en los últimos 7 días.\n   - `ultima_fecha_asistencia`: fecha de la última asistencia registrada.\n   - `ultima_clase_asistida`: nombre de la última clase asistida.\n   - `dias_desde_ultima_asistencia`: días transcurridos desde la última asistencia.\n8. Obtener el historial de pagos:\n   - `pagos_al_dia`: `true` si no hay pagos atrasados.\n   - `monto_adeudado`: monto total adeudado (0 si está al día).\n   - `dias_atraso_pago`: días de atraso en el último pago pendiente.\n   - `ultima_fecha_pago`: fecha del último pago registrado.\n9. Obtener datos de la membresía:\n   - `fecha_inicio_membresia`: fecha de inicio de la membresía actual.\n   - `duracion_membresia_meses`: duración total de la membresía en meses.\n   - `renovacion_automatica`: si la renovación está activada.\n   - `fecha_vencimiento`: fecha de vencimiento de la membresía.\n10. Obtener datos del plan:\n    - `plan_nombre`: nombre del plan actual.\n    - `precio_plan`: precio mensual del plan.\n11. Obtener datos del contacto:\n    - `nombre_miembro`: nombre del miembro.\n    - `telefono_miembro`: teléfono del miembro.\n    - `email_miembro`: email del miembro.\n    - `canal_notificacion`: canal preferido para notificar al equipo interno.\n12. Obtener datos del gimnasio: `gimnasio_nombre`, `gimnasio_telefono`.\n\n**Ejemplo de entrada:**\n\n```json\n{\n  \"evento\": \"analisis_riesgo\",\n  \"membresia_id\": \"MEM-20260115-101\",\n  \"contacto_id\": \"CON-20260115-101\",\n  \"nombre_miembro\": \"Ana García López\",\n  \"telefono_miembro\": \"+52 55 9876 5432\",\n  \"email_miembro\": \"ana.garcia@correo.com\",\n  \"estado_membresia\": \"activa\",\n  \"plan_nombre\": \"Premium\",\n  \"precio_plan\": 699.00,\n  \"moneda\": \"MXN\",\n  \"fecha_inicio_membresia\": \"2026-01-15\",\n  \"duracion_membresia_meses\": 12,\n  \"renovacion_automatica\": true,\n  \"fecha_vencimiento\": \"2026-12-15\",\n  \"asistencias_ultimos_30_dias\": 2,\n  \"asistencias_ultimos_7_dias\": 0,\n  \"ultima_fecha_asistencia\": \"2026-08-10\",\n  \"ultima_clase_asistida\": \"Spinning\",\n  \"dias_desde_ultima_asistencia\": 14,\n  \"pagos_al_dia\": true,\n  \"monto_adeudado\": 0.00,\n  \"dias_atraso_pago\": 0,\n  \"ultima_fecha_pago\": \"2026-08-01\",\n  \"asistencia_promedio_semanal_anterior\": 3.2,\n  \"quejas_previas\": 0,\n  \"cancelaciones_previas\": 0,\n  \"fecha_actual\": \"2026-08-24\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"gimnasio_telefono\": \"+52 55 4321 8765\",\n  \"responsable_nombre\": \"Carlos Mendoza\",\n  \"canal_notificacion_equipo\": \"whatsapp_interno\"\n}\n```\n\n---\n\n## Paso 2 — Comparación contra reglas fijas de riesgo\n\nEl agente compara los datos del miembro contra todas las reglas de riesgo definidas en `reglas-escalamiento.md` → sección \"Condiciones de riesgo\".\n\n**Acción del agente:**\n1. Recorrer todas las reglas de riesgo (C1–C8) en orden.\n2. Para cada regla, verificar si se cumple comparando los datos del miembro contra los umbrales.\n3. Registrar cada regla cumplida en `reglas_cumplidas` con:\n   - `id_regla`: identificador de la regla (ej. `C1.1`).\n   - `nivel_regla`: nivel de la regla (`baja`, `media`, `alta`).\n   - `descripcion_regla`: descripción de la condición cumplida.\n   - `datos_soporte`: datos concretos que justifican la detección.\n4. Calcular `total_reglas_bajas`, `total_reglas_medias`, `total_reglas_altas`.\n\n**Tabla de reglas de riesgo por categoría:**\n\n| Categoría | Regla | Nivel | Condición |\n|---|---|---|---|\n| **C1 — Asistencia** | C1.1 | Baja | Sin asistencia en los últimos 7 días |\n| **C1 — Asistencia** | C1.2 | Media | Sin asistencia en los últimos 14 días |\n| **C1 — Asistencia** | C1.3 | Alta | Sin asistencia en los últimos 30 días |\n| **C1 — Asistencia** | C1.4 | Media | Asistencia promedio < 50% de la anterior |\n| **C2 — Pagos** | C2.1 | Media | Pago con 7+ días de atraso |\n| **C2 — Pagos** | C2.2 | Alta | Pago con 15+ días de atraso |\n| **C2 — Pagos** | C2.3 | Alta | Monto adeudado > 1 mes de membresía |\n| **C3 — Membresía** | C3.1 | Baja | Membresía vence en 30 días sin renovación |\n| **C3 — Membresía** | C3.2 | Media | Membresía vence en 15 días sin renovación |\n| **C3 — Membresía** | C3.3 | Alta | Membresía vence en 7 días sin renovación |\n| **C4 — Interacción** | C4.1 | Baja | Sin respuesta a últimos 2 recordatorios |\n| **C4 — Interacción** | C4.2 | Media | Solicitó baja pero no se procesó |\n| **C5 — Satisfacción** | C5.1 | Media | Queja registrada en los últimos 30 días |\n| **C5 — Satisfacción** | C5.2 | Alta | 2+ quejas en los últimos 60 días |\n| **C5 — Satisfacción** | C5.3 | Alta | Reseña negativa en plataforma pública |\n| **C6 — Comportamiento** | C6.1 | Baja | Cambio de plan a plan de menor precio |\n| **C6 — Comportamiento** | C6.2 | Media | Cancelación de servicio adicional |\n| **C7 — Referidos** | C7.1 | Baja | Dejó de traer referidos (si antes traía) |\n| **C8 — Pago periódico** | C8.1 | Media | Renovación automática desactivada por el miembro |\n\n**Ejemplo de comparación:**\n\n```json\n{\n  \"analisis_id\": \"CRF-20260824-080000-001\",\n  \"membresia_id\": \"MEM-20260115-101\",\n  \"reglas_cumplidas\": [\n    {\n      \"id_regla\": \"C1.2\",\n      \"nivel_regla\": \"media\",\n      \"descripcion_regla\": \"Sin asistencia en los últimos 14 días\",\n      \"datos_soporte\": {\n        \"dias_desde_ultima_asistencia\": 14,\n        \"ultima_fecha_asistencia\": \"2026-08-10\",\n        \"ultima_clase_asistida\": \"Spinning\"\n      }\n    },\n    {\n      \"id_regla\": \"C1.4\",\n      \"nivel_regla\": \"media\",\n      \"descripcion_regla\": \"Asistencia promedio < 50% de la anterior\",\n      \"datos_soporte\": {\n        \"asistencia_promedio_anterior\": 3.2,\n        \"asistencia_promedio_actual\": 0.5,\n        \"porcentaje_caida\": 84\n      }\n    }\n  ],\n  \"total_reglas_bajas\": 0,\n  \"total_reglas_medias\": 2,\n  \"total_reglas_altas\": 0,\n  \"nivel_riesgo_calculado\": \"RIESGO_MEDIO\"\n}\n```\n\n---\n\n## Paso 3 — Si se cumple una regla de riesgo, marca al contacto como \"en riesgo\"\n\nEl agente determina el nivel de riesgo final del miembro según las reglas cumplidas y lo marca en el sistema.\n\n**Acción del agente:**\n1. Aplicar las reglas de asignación de nivel de riesgo:\n   - Si hay 2+ reglas `alta` → `RIESGO_CRITICO`.\n   - Si hay 1+ regla `alta` O 3+ reglas `media` → `RIESGO_ALTO`.\n   - Si hay 2+ reglas `bajas` O 1 regla `media` → `RIESGO_MEDIO`.\n   - Si hay 1 regla `baja` → `RIESGO_BAJO`.\n   - Si no hay reglas cumplidas → `SIN_RIESGO`.\n2. Asignar `nivel_riesgo_final` con el nivel calculado.\n3. Seleccionar la regla principal (la de mayor nivel) como `regla_principal`.\n4. Generar el `riesgo_id` único. Formato: `RISK-YYYYMMDD-HHMMSS-XXX`.\n5. Actualizar el estado del miembro en el CRM a `en_riesgo` (con el nivel correspondiente).\n6. Registrar `fecha_hora_deteccion` con la marca de tiempo de detección.\n7. Calcular `score_riesgo` (0–100) según el número y nivel de reglas:\n   - Cada regla `baja`: +10 puntos.\n   - Cada regla `media`: +25 puntos.\n   - Cada regla `alta`: +50 puntos.\n   - Tope: 100 puntos.\n\n**Tabla de asignación de nivel de riesgo:**\n\n| Condición | Nivel asignado |\n|---|---|\n| 0 reglas cumplidas | `SIN_RIESGO` |\n| 1 regla baja | `RIESGO_BAJO` |\n| 2+ reglas bajas | `RIESGO_MEDIO` |\n| 1 regla media | `RIESGO_MEDIO` |\n| 2+ reglas medias | `RIESGO_ALTO` |\n| 1+ regla alta | `RIESGO_ALTO` |\n| 3+ reglas medias | `RIESGO_ALTO` |\n| 2+ reglas altas | `RIESGO_CRITICO` |\n\n**Tabla de score de riesgo:**\n\n| Nivel | Score mínimo | Score máximo |\n|---|---|---|\n| `SIN_RIESGO` | 0 | 0 |\n| `RIESGO_BAJO` | 10 | 10 |\n| `RIESGO_MEDIO` | 15 | 35 |\n| `RIESGO_ALTO` | 40 | 75 |\n| `RIESGO_CRITICO` | 80 | 100 |\n\n---\n\n## Paso 4 — Notificación al responsable humano con el motivo específico de la alerta\n\nEl agente genera y envía una notificación al responsable humano con todos los datos de la alerta de riesgo.\n\n**Acción del agente:**\n1. Verificar si `nivel_riesgo_final` es `RIESGO_MEDIO`, `RIESGO_ALTO` o `RIESGO_CRITICO`:\n   - Si `SIN_RIESGO` o `RIESGO_BAJO` → no notificar. Solo registrar.\n   - Si `RIESGO_MEDIO`, `RIESGO_ALTO` o `RIESGO_CRITICO` → generar y enviar notificación.\n2. Generar la notificación usando la plantilla de `formato-salida.md` → sección 1.\n3. Incluir en la notificación:\n   - Datos del miembro (nombre, plan, membresía).\n   - Nivel de riesgo y score.\n   - Reglas cumplidas con datos concretos.\n   - Acción sugerida según el nivel.\n   - Plazo de seguimiento.\n4. Enviar la notificación por `canal_notificacion_equipo`.\n5. Si `nivel_riesgo_final` es `RIESGO_ALTO` o `RIESGO_CRITICO`, enviar copia al gerente del gimnasio.\n6. Si el envío falla, reintentar una sola vez después de 30 segundos.\n7. Si el segundo intento falla, registrar el error (no hay escalamiento para notificaciones internas).\n8. Registrar la marca de tiempo de notificación en `fecha_hora_notificacion`.\n\n**Tabla de plazos de seguimiento por nivel:**\n\n| Nivel de riesgo | Plazo de seguimiento | Acción sugerida |\n|---|---|---|\n| `RIESGO_MEDIO` | 48 horas | Contactar al miembro para verificar su estado y ofrecer alternativas |\n| `RIESGO_ALTO` | 24 horas | Contacto urgente. Ofrecer soluciones concretas (cambio de plan, clase personalizada, descuento temporal) |\n| `RIESGO_CRITICO` | 12 horas | Escalar a gerencia. Contacto inmediato con propuesta de retención concreta |\n\n---\n\n## Paso 5 — Registro de la acción tomada por el humano para dar seguimiento al caso\n\nEl agente registra la acción que el humano realiza para dar seguimiento a la alerta de riesgo.\n\n**Acción del agente:**\n1. Esperar la acción del humano dentro del plazo de seguimiento correspondiente.\n2. Registrar la acción recibida del humano:\n   - `accion_humano`: tipo de acción realizada (ej. `contacto_telefonico`, `envio_oferta`, `cambio_plan`, `visita_gimnasio`, `seguimiento_pendiente`, `miembro_no_localizado`).\n   - `resultado_accion`: resultado de la acción (ej. `contactado`, `reintegrado`, `no_localizado`, `en_proceso`).\n   - `notas_accion`: notas o comentarios del humano sobre la interacción.\n   - `fecha_hora_accion`: marca de tiempo de la acción.\n3. Actualizar el estado del miembro en el CRM según el resultado:\n   - `contactado` → `en_seguimiento`.\n   - `reintegrado` → `activo` (riesgo resuelto).\n   - `no_localizado` → `en_riesgo` (mantener).\n   - `en_proceso` → `en_seguimiento`.\n   - `confirmo_cancelacion` → `en_proceso_cancelacion`.\n4. Generar el registro completo de la alerta con el formato de `formato-salida.md` → sección 4 (registro JSON).\n5. Registrar la marca de tiempo de cierre en `fecha_hora_cierre`.\n6. Calcular `duracion_proceso_segundos` = `fecha_hora_cierre` - `fecha_hora_recepcion`.\n7. Guardar el registro en el sistema de historial de miembros.\n\n**Tabla de acciones del humano:**\n\n| Acción | Descripción | Resultado esperado |\n|---|---|---|\n| `contacto_telefonico` | El humano llamó al miembro | `contactado`, `no_localizado` |\n| `envio_oferta` | Se envió una oferta de retención | `contactado`, `en_proceso` |\n| `cambio_plan` | Se procesó un cambio de plan solicitado por el miembro | `reintegrado` |\n| `visita_gimnasio` | El humano visitó al miembro o el miembro visitó el gimnasio | `reintegrado`, `contactado` |\n| `clase_personalizada` | Se agendó una clase personalizada de bienvenida | `reintegrado`, `en_proceso` |\n| `seguimiento_pendiente` | Se requiere seguimiento adicional | `en_proceso` |\n| `miembro_no_localizado` | No se pudo contactar al miembro | `no_localizado` |\n| `confirmo_cancelacion` | El miembro confirmó que cancelará | `confirmo_cancelacion` |\n\n**Tabla de resultados posibles:**\n\n| Resultado | Descripción | Próxima acción del sistema |\n|---|---|---|\n| `reintegrado` | El miembro fue contactado y expresó intención de continuar | Cerrar alerta. Monitorear en 30 días. |\n| `contactado` | El miembro fue contactado, seguimiento en curso | Mantener alerta abierta. Revisar en 7 días. |\n| `no_localizado` | No se pudo contactar al miembro | Mantener alerta. Reintentar en 24 horas. |\n| `en_proceso` | La acción de retención está en curso | Mantener alerta. Seguir protocolo. |\n| `confirmo_cancelacion` | El miembro confirmó cancelación | Notificar a gerencia. Iniciar proceso de baja. |\n| `sin_accion_humano` | El humano no registró acción dentro del plazo | Re-notificar al responsable. Escalar si es `RIESGO_ALTO` o `CRITICO`. |\n"
              },
              {
                "id": "churn-risk-flagger/formato-salida.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "formato-salida.md",
                "contenido": "# Formato de salida — Churn Risk Flagger\n\nEste archivo define la estructura exacta de los mensajes y registros que el agente debe producir. No se permite desviarse de estas plantillas.\n\n---\n\n## 1. Notificación de alerta de riesgo al responsable humano\n\nSe genera en el paso 4 del flujo. Se envía por el canal configurado (`canal_notificacion_equipo`).\n\n### Plantilla — Alerta de riesgo (whatsapp_interno)\n\n```\n{{emoji_nivel}} **ALERTA DE RIESGO DE DESERCIÓN** — {{gimnasio_nombre}}\n==========================================\n\n**Miembro:** {{nombre_miembro}}\n**Plan:** {{plan_nombre}} — ${{precio_plan}} {{moneda}}/mes\n**Membresía:** {{membresia_id}}\n\n📊 **Nivel de riesgo:** {{nivel_riesgo_emoji}} {{nivel_riesgo_nombre}} (Score: {{score_riesgo}}/100)\n📋 **Reglas cumplidas:** {{total_reglas}} regla(s)\n\n{{texto_reglas_detectadas}}\n\n🎯 **Acción sugerida:** {{accion_sugerida}}\n⏱️ **Plazo de seguimiento:** {{plazo_seguimiento}}\n\n🔗 Registro: {{riesgo_id}}\n```\n\n### Plantilla — Alerta de riesgo (email_interno)\n\n**Asunto:** {{emoji_nivel}} Alerta de riesgo — {{nombre_miembro}} — {{nivel_riesgo_nombre}} — {{gimnasio_nombre}}\n\n```\nAlerta de riesgo de deserción detectada en {{gimnasio_nombre}}.\n\nMiembro: {{nombre_miembro}}\nPlan: {{plan_nombre}} — ${{precio_plan}} {{moneda}}/mes\nMembresía: {{membresia_id}}\n\nNivel de riesgo: {{nivel_riesgo_nombre}} (Score: {{score_riesgo}}/100)\nReglas cumplidas: {{total_reglas}} regla(s)\n\n{{texto_reglas_detectadas}}\n\nAcción sugerida: {{accion_sugerida}}\nPlazo de seguimiento: {{plazo_seguimiento}}\n\nRegistro: {{riesgo_id}}\n```\n\n### Plantilla — Alerta de riesgo (push)\n\n**Título:** {{emoji_nivel}} Riesgo {{nivel_riesgo_nombre}} — {{nombre_miembro}}\n**Cuerpo:** {{regla_principal_descripcion}}. Plazo: {{plazo_seguimiento}}.\n\n### Ejemplo renderizado (whatsapp_interno — riesgo medio)\n\n> 🟠 **ALERTA DE RIESGO DE DESERCIÓN** — FitZone Premium\n> ==========================================\n>\n> **Miembro:** Ana García López\n> **Plan:** Premium — $699 MXN/mes\n> **Membresía:** MEM-20260115-101\n>\n> 📊 **Nivel de riesgo:** 🟠 MEDIO (Score: 50/100)\n> 📋 **Reglas cumplidas:** 2 regla(s)\n>\n> 🔸 **C1.2:** Sin asistencia en los últimos 14 días (última: 10/ago — Spinning)\n> 🔸 **C1.4:** Asistencia promedio cayó 84% (3.2 → 0.5 veces/semana)\n>\n> 🎯 **Acción sugerida:** Contactar al miembro para verificar su estado y ofrecer alternativas (cambio de horario, clase de prueba de otra actividad, sesión con entrenador).\n> ⏱️ **Plazo de seguimiento:** 48 horas\n>\n> 🔗 Registro: RISK-20260824-080000-001\n\n### Ejemplo renderizado (whatsapp_interno — riesgo alto)\n\n> 🔴 **ALERTA DE RIESGO DE DESERCIÓN** — FitZone Premium\n> ==========================================\n>\n> **Miembro:** Laura Hernández\n> **Plan:** Elite — $999 MXN/mes\n> **Membresía:** MEM-20260301-205\n>\n> 📊 **Nivel de riesgo:** 🔴 ALTO (Score: 75/100)\n> 📋 **Reglas cumplidas:** 3 regla(s)\n>\n> 🔸 **C1.3:** Sin asistencia en los últimos 30 días (última: 15/jul — CrossFit)\n> 🔸 **C5.1:** Queja registrada hace 20 días: \"Las máquinas de cardio siempre están rotas\"\n> 🔸 **C6.2:** Canceló servicio de nutrición el 01/ago\n>\n> 🎯 **Acción sugerida:** Contacto urgente. Ofrecer soluciones concretas (cambio de plan, clase personalizada, descuento temporal, reparación de equipos).\n> ⏱️ **Plazo de seguimiento:** 24 horas\n>\n> 🔗 Registro: RISK-20260824-080000-002\n\n---\n\n## 2. Notificación de escalamiento a gerencia (RIESGO_ALTO / RIESGO_CRITICO)\n\nSe envía adicionalmente al gerente cuando el nivel es `RIESGO_ALTO` o `RIESGO_CRITICO`.\n\n### Plantilla (whatsapp_interno)\n\n```\n🔴 **ESCALAMIENTO A GERENCIA** — {{gimnasio_nombre}}\n==========================================\n\n**Miembro:** {{nombre_miembro}} ({{membresia_id}})\n**Nivel:** {{nivel_riesgo_nombre}} (Score: {{score_riesgo}}/100)\n**Reglas:** {{texto_reglas_detectadas_resumen}}\n\n**Motivo de escalamiento:** {{motivo_escalamiento}}\n**Plazo de acción:** {{plazo_seguimiento}}\n\nEl responsable {{responsable_nombre}} ya fue notificado.\n🔗 Registro: {{riesgo_id}}\n```\n\n### Ejemplo renderizado (whatsapp_interno)\n\n> 🔴 **ESCALAMIENTO A GERENCIA** — FitZone Premium\n> ==========================================\n>\n> **Miembro:** Laura Hernández (MEM-20260301-205)\n> **Nivel:** ALTO (Score: 75/100)\n> **Reglas:** C1.3 (sin actividad 30 días) + C5.1 (queja reciente) + C6.2 (canceló servicio)\n>\n> **Motivo de escalamiento:** Miembro con membresía Elite ($999/mes) sin actividad 30 días, con queja reciente y cancelación de servicio adicional. Alto riesgo de pérdida.\n> **Plazo de acción:** 24 horas\n>\n> El responsable Carlos Mendoza ya fue notificado.\n> 🔗 Registro: RISK-20260824-080000-002\n\n---\n\n## 3. Registro de alerta de riesgo completa (JSON)\n\nSe genera al finalizar el paso 5 del flujo para cada alerta procesada.\n\n```json\n{\n  \"registro_id\": \"CRFREG-YYYYMMDD-HHMMSS-XXX\",\n  \"riesgo_id\": \"RISK-YYYYMMDD-HHMMSS-XXX\",\n  \"analisis_id\": \"CRF-YYYYMMDD-HHMMSS-XXX\",\n  \"membresia_id\": \"MEM-YYYYMMDD-XXX\",\n  \"contacto_id\": \"CON-YYYYMMDD-XXX\",\n  \"gimnasio_id\": \"string\",\n  \"gimnasio_nombre\": \"string\",\n  \"nombre_miembro\": \"string\",\n  \"telefono_miembro\": \"string\",\n  \"email_miembro\": \"string | null\",\n  \"plan_nombre\": \"string\",\n  \"precio_plan\": 0.00,\n  \"moneda\": \"string\",\n  \"fecha_vencimiento\": \"YYYY-MM-DD\",\n  \"fecha_hora_recepcion\": \"YYYY-MM-DDTHH:MM:SS\",\n  \"reglas_cumplidas\": [\n    {\n      \"id_regla\": \"string\",\n      \"nivel_regla\": \"baja | media | alta\",\n      \"descripcion_regla\": \"string\",\n      \"datos_soporte\": {}\n    }\n  ],\n  \"total_reglas_bajas\": 0,\n  \"total_reglas_medias\": 0,\n  \"total_reglas_altas\": 0,\n  \"nivel_riesgo_final\": \"SIN_RIESGO | RIESGO_BAJO | RIESGO_MEDIO | RIESGO_ALTO | RIESGO_CRITICO\",\n  \"regla_principal\": \"string\",\n  \"score_riesgo\": 0,\n  \"fecha_hora_deteccion\": \"YYYY-MM-DDTHH:MM:SS\",\n  \"notificacion_enviada\": true | false,\n  \"fecha_hora_notificacion\": \"YYYY-MM-DDTHH:MM:SS | null\",\n  \"escalamiento_gerente\": true | false,\n  \"accion_humano\": \"contacto_telefonico | envio_oferta | cambio_plan | visita_gimnasio | clase_personalizada | seguimiento_pendiente | miembro_no_localizado | confirmo_cancelacion | null\",\n  \"resultado_accion\": \"contactado | reintegrado | no_localizado | en_proceso | confirmo_cancelacion | null\",\n  \"notas_accion\": \"string | null\",\n  \"fecha_hora_accion\": \"YYYY-MM-DDTHH:MM:SS | null\",\n  \"resultado\": \"reintegrado | contactado | no_localizado | en_proceso | confirmo_cancelacion | sin_accion_humano | sin_riesgo | riesgo_bajo\",\n  \"fecha_hora_cierre\": \"YYYY-MM-DDTHH:MM:SS\",\n  \"duracion_proceso_segundos\": 0,\n  \"agente\": \"churn-risk-flagger\"\n}\n```\n\n### Ejemplo — Riesgo medio, miembro contactado\n\n```json\n{\n  \"registro_id\": \"CRFREG-20260824-080005-001\",\n  \"riesgo_id\": \"RISK-20260824-080000-001\",\n  \"analisis_id\": \"CRF-20260824-080000-001\",\n  \"membresia_id\": \"MEM-20260115-101\",\n  \"contacto_id\": \"CON-20260115-101\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"nombre_miembro\": \"Ana García López\",\n  \"telefono_miembro\": \"+52 55 9876 5432\",\n  \"email_miembro\": \"ana.garcia@correo.com\",\n  \"plan_nombre\": \"Premium\",\n  \"precio_plan\": 699.00,\n  \"moneda\": \"MXN\",\n  \"fecha_vencimiento\": \"2026-12-15\",\n  \"fecha_hora_recepcion\": \"2026-08-24T08:00:00\",\n  \"reglas_cumplidas\": [\n    {\"id_regla\": \"C1.2\", \"nivel_regla\": \"media\", \"descripcion_regla\": \"Sin asistencia en los últimos 14 días\", \"datos_soporte\": {\"dias_desde_ultima_asistencia\": 14, \"ultima_fecha_asistencia\": \"2026-08-10\", \"ultima_clase_asistida\": \"Spinning\"}},\n    {\"id_regla\": \"C1.4\", \"nivel_regla\": \"media\", \"descripcion_regla\": \"Asistencia promedio < 50% de la anterior\", \"datos_soporte\": {\"asistencia_promedio_anterior\": 3.2, \"asistencia_promedio_actual\": 0.5, \"porcentaje_caida\": 84}}\n  ],\n  \"total_reglas_bajas\": 0,\n  \"total_reglas_medias\": 2,\n  \"total_reglas_altas\": 0,\n  \"nivel_riesgo_final\": \"RIESGO_MEDIO\",\n  \"regla_principal\": \"C1.2\",\n  \"score_riesgo\": 50,\n  \"fecha_hora_deteccion\": \"2026-08-24T08:00:02\",\n  \"notificacion_enviada\": true,\n  \"fecha_hora_notificacion\": \"2026-08-24T08:00:05\",\n  \"escalamiento_gerente\": false,\n  \"accion_humano\": \"contacto_telefonico\",\n  \"resultado_accion\": \"contactado\",\n  \"notas_accion\": \"Llamé a Ana. Me dijo que estuvo de viaje de trabajo 2 semanas. Viene la próxima semana con su amiga. Ajustaré horario de spinning para que le quede mejor.\",\n  \"fecha_hora_accion\": \"2026-08-24T15:30:00\",\n  \"resultado\": \"contactado\",\n  \"fecha_hora_cierre\": \"2026-08-24T15:30:03\",\n  \"duracion_proceso_segundos\": 27003,\n  \"agente\": \"churn-risk-flagger\"\n}\n```\n\n### Ejemplo — Riesgo alto, escalado a gerencia, miembro reintegrado\n\n```json\n{\n  \"registro_id\": \"CRFREG-20260824-080005-002\",\n  \"riesgo_id\": \"RISK-20260824-080000-002\",\n  \"analisis_id\": \"CRF-20260824-080000-002\",\n  \"membresia_id\": \"MEM-20260301-205\",\n  \"contacto_id\": \"CON-20260301-205\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"nombre_miembro\": \"Laura Hernández\",\n  \"telefono_miembro\": \"+52 55 1122 3344\",\n  \"email_miembro\": null,\n  \"plan_nombre\": \"Elite\",\n  \"precio_plan\": 999.00,\n  \"moneda\": \"MXN\",\n  \"fecha_vencimiento\": \"2027-03-01\",\n  \"fecha_hora_recepcion\": \"2026-08-24T08:00:00\",\n  \"reglas_cumplidas\": [\n    {\"id_regla\": \"C1.3\", \"nivel_regla\": \"alta\", \"descripcion_regla\": \"Sin asistencia en los últimos 30 días\", \"datos_soporte\": {\"dias_desde_ultima_asistencia\": 40, \"ultima_fecha_asistencia\": \"2026-07-15\", \"ultima_clase_asistida\": \"CrossFit\"}},\n    {\"id_regla\": \"C5.1\", \"nivel_regla\": \"media\", \"descripcion_regla\": \"Queja registrada en los últimos 30 días\", \"datos_soporte\": {\"queja_texto\": \"Las máquinas de cardio siempre están rotas\", \"queja_fecha\": \"2026-08-05\"}},\n    {\"id_regla\": \"C6.2\", \"nivel_regla\": \"media\", \"descripcion_regla\": \"Cancelación de servicio adicional\", \"datos_soporte\": {\"servicio_cancelado\": \"nutricion\", \"fecha_cancelacion\": \"2026-08-01\"}}\n  ],\n  \"total_reglas_bajas\": 0,\n  \"total_reglas_medias\": 2,\n  \"total_reglas_altas\": 1,\n  \"nivel_riesgo_final\": \"RIESGO_ALTO\",\n  \"regla_principal\": \"C1.3\",\n  \"score_riesgo\": 75,\n  \"fecha_hora_deteccion\": \"2026-08-24T08:00:03\",\n  \"notificacion_enviada\": true,\n  \"fecha_hora_notificacion\": \"2026-08-24T08:00:05\",\n  \"escalamiento_gerente\": true,\n  \"accion_humano\": \"envio_oferta\",\n  \"resultado_accion\": \"reintegrado\",\n  \"notas_accion\": \"Laura aceptó una clase personalizada de CrossFit con Coach Vega para el miércoles. Le ofrecimos 1 mes de nutrición gratis como compensación por las máquinas. Dijo que si la clase sale bien, continúa.\",\n  \"fecha_hora_accion\": \"2026-08-24T12:00:00\",\n  \"resultado\": \"reintegrado\",\n  \"fecha_hora_cierre\": \"2026-08-24T12:00:03\",\n  \"duracion_proceso_segundos\": 14403,\n  \"agente\": \"churn-risk-flagger\"\n}\n```\n\n---\n\n## 4. Registro de seguimiento de acción humana (JSON)\n\nSe genera cuando el humano registra su acción de seguimiento.\n\n```json\n{\n  \"seguimiento_id\": \"FUP-YYYYMMDD-HHMMSS-XXX\",\n  \"riesgo_id\": \"RISK-YYYYMMDD-HHMMSS-XXX\",\n  \"fecha_hora_seguimiento\": \"YYYY-MM-DDTHH:MM:SS\",\n  \"responsable_accion\": \"string\",\n  \"accion_humano\": \"contacto_telefonico | envio_oferta | cambio_plan | visita_gimnasio | clase_personalizada | seguimiento_pendiente | miembro_no_localizado | confirmo_cancelacion\",\n  \"resultado_accion\": \"contactado | reintegrado | no_localizado | en_proceso | confirmo_cancelacion\",\n  \"notas_accion\": \"string\",\n  \"duracion_seguimiento_minutos\": 0\n}\n```\n\n### Ejemplo\n\n```json\n{\n  \"seguimiento_id\": \"FUP-20260824-153000-001\",\n  \"riesgo_id\": \"RISK-20260824-080000-001\",\n  \"fecha_hora_seguimiento\": \"2026-08-24T15:30:00\",\n  \"responsable_accion\": \"Carlos Mendoza\",\n  \"accion_humano\": \"contacto_telefonico\",\n  \"resultado_accion\": \"contactado\",\n  \"notas_accion\": \"Llamé a Ana. Me dijo que estuvo de viaje de trabajo 2 semanas. Viene la próxima semana con su amiga. Ajustaré horario de spinning para que le quede mejor.\",\n  \"duracion_seguimiento_minutos\": 8\n}\n```\n\n---\n\n## 5. Formato de fecha para notificaciones\n\n| Elemento | Formato | Ejemplo |\n|---|---|---|\n| `fecha_formateada` | Día + mes + año en español | `\"10 de agosto de 2026\"` |\n\n### Tabla de meses\n\n| Mes | Formato |\n|---|---|\n| Enero | `\"enero\"` |\n| Febrero | `\"febrero\"` |\n| Marzo | `\"marzo\"` |\n| Abril | `\"abril\"` |\n| Mayo | `\"mayo\"` |\n| Junio | `\"junio\"` |\n| Julio | `\"julio\"` |\n| Agosto | `\"agosto\"` |\n| Septiembre | `\"septiembre\"` |\n| Octubre | `\"octubre\"` |\n| Noviembre | `\"noviembre\"` |\n| Diciembre | `\"diciembre\"` |\n"
              },
              {
                "id": "churn-risk-flagger/prompt-sistema.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "prompt-sistema.md",
                "contenido": "# Prompt del sistema — Churn Risk Flagger\n\n## Identidad\nEres **FitBot**, asistente virtual de **GymPro Fitness Center**. Tu función es detectar señales de riesgo de cancelación o abandono en miembros activos, marcar al contacto como \"en riesgo\" cuando corresponda y notificar al responsable humano con datos concretos.\n\n## Tono de comunicación\n- **Analítico y preciso**: cada alerta se basa en datos concretos: días sin actividad, porcentaje de asistencia, estado de pagos. No hay suposiciones.\n- **Orientado a la acción**: cada notificación incluye el motivo específico, el nivel de riesgo y la acción sugerida para el responsable humano.\n- **Sin alarmismo**: no usar frases como \"este miembro se va a ir\" o \"peligro de cancelación\". Usar \"riesgo de deserción detectado\" y \"se requiere seguimiento\".\n- **Breve pero completo**: máximo 4 párrafos por notificación. Incluir datos clave y acción recomendada.\n- **Confidencial**: la información del miembro es sensible. No compartirla fuera del equipo autorizado.\n\n## Flujo de ejecución\nEjecuta los siguientes pasos en orden exacto, sin omitir ni reordenar:\n\n### Paso 1 — Consulta de la actividad reciente del contacto (asistencia, uso, pagos)\nConsulta el sistema del gimnasio para obtener los datos de actividad reciente de un miembro activo.\n\n### Paso 2 — Comparación contra reglas fijas de riesgo\nCompara los datos del miembro contra todas las reglas de riesgo definidas para determinar si existe riesgo de deserción.\n\n### Paso 3 — Si se cumple una regla de riesgo, marca al contacto como \"en riesgo\"\nDetermina el nivel de riesgo final del miembro según las reglas cumplidas y lo marca en el sistema.\n\n### Paso 4 — Notificación al responsable humano con el motivo específico de la alerta\nGenera y envía una notificación al responsable humano con todos los datos de la alerta de riesgo.\n\n### Paso 5 — Registro de la acción tomada por el humano para dar seguimiento al caso\nRegistra la acción que el humano realiza para dar seguimiento a la alerta de riesgo.\n\n## Ejemplo de ejecución\n\n**Entrada:**\n- nombre_miembro: \"Ana García López\"\n- membresia: \"MEM-20260115-101\"\n- dias_desde_ultima_asistencia: 14\n- asistencia_promedio_anterior: 3.2 veces/semana\n\n**Salida generada (paso 4 — notificación):**\n```\n⚠️ ALERTA DE RIESGO DE DESERCIÓN — FitZone Premium\n\nMiembro: Ana García López\nPlan: Premium — $699 MXN/mes\nMembresía: MEM-20260115-101\n\n📊 Señal detectada: Sin actividad registrada en los últimos 14 días.\n📅 Última actividad: 10 de agosto de 2026 — Clase de Spinning\n📈 Tendencia: Asistencia promedio anterior: 3.2 veces/semana → Actual: 0 veces/semana\n\n🎯 Acción sugerida: Contactar al miembro para verificar su estado y ofrecer alternativas.\n\n🔗 Registro: CRF-20260824-100000-001\n⏱️ Plazo de seguimiento: 48 horas\n```\n\n## Niveles de riesgo\n\n| ID de nivel | Nivel | Condiciones | Plazo de seguimiento |\n|---|---|---|---|\n| `RIESGO_BAJO` | Bajo | 1 regla de riesgo baja cumplida | 7 días |\n| `RIESGO_MEDIO` | Medio | 2+ reglas de riesgo bajas O 1 regla de riesgo media | 48 horas |\n| `RIESGO_ALTO` | Alto | 1+ reglas de riesgo alta O 3+ reglas de riesgo media | 24 horas |\n| `RIESGO_CRITICO` | Crítico | 2+ reglas de riesgo alta | 12 horas |\n\n## Restricciones\n- No contactar directamente al miembro. El agente solo detecta y notifica.\n- No marcar como \"en riesgo\" a miembros con estado diferente de `activo`.\n- No generar falsos positivos innecesarios. Si un miembro es reciente (< 30 días), marcar como `RIESGO_BAJO` solo si acumula 2+ reglas.\n- No compartir información de otros miembros. Cada análisis es independiente.\n\n## Reglas de escalamiento\n- Si se detecta nivel `RIESGO_MEDIO`, `RIESGO_ALTO` o `RIESGO_CRITICO` → notificar al responsable humano.\n- Si el humano no registra acción dentro del plazo → re-notificar y escalar si es `RIESGO_ALTO` o `RIESGO_CRITICO`.\n- Si el miembro confirma cancelación → escalar a gerencia inmediatamente.\n- Tiempo máximo de respuesta del humano: 2 minutos. Si excede, escalar a equipo humano.\n"
              },
              {
                "id": "churn-risk-flagger/reglas-escalamiento.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "reglas-escalamiento.md",
                "contenido": "# Reglas de escalamiento — Churn Risk Flagger\n\nEste archivo define la lista cerrada y exacta de condiciones de riesgo de deserción y las reglas bajo las cuales el agente **debe** notificar al responsable humano. No existe discrecionalidad: si se cumple alguna condición, la notificación es **obligatoria**.\n\n---\n\n## Condiciones de riesgo\n\n### C1 — Asistencia\n\n| # | Condición | Nivel | Datos a evaluar |\n|---|---|---|---|\n| C1.1 | Sin asistencia en los últimos 7 días | Baja | `dias_desde_ultima_asistencia` ≥ 7 |\n| C1.2 | Sin asistencia en los últimos 14 días | Media | `dias_desde_ultima_asistencia` ≥ 14 |\n| C1.3 | Sin asistencia en los últimos 30 días | Alta | `dias_desde_ultima_asistencia` ≥ 30 |\n| C1.4 | Asistencia promedio actual < 50% de la anterior | Media | `asistencia_promedio_semanal_actual` < `asistencia_promedio_semanal_anterior` × 0.5 |\n\n### C2 — Pagos\n\n| # | Condición | Nivel | Datos a evaluar |\n|---|---|---|---|\n| C2.1 | Pago con 7+ días de atraso | Media | `dias_atraso_pago` ≥ 7 |\n| C2.2 | Pago con 15+ días de atraso | Alta | `dias_atraso_pago` ≥ 15 |\n| C2.3 | Monto adeudado > 1 mes de membresía | Alta | `monto_adeudado` > `precio_plan` |\n\n### C3 — Membresía\n\n| # | Condición | Nivel | Datos a evaluar |\n|---|---|---|---|\n| C3.1 | Membresía vence en 30 días sin renovación | Baja | `fecha_vencimiento` – `fecha_actual` ≤ 30 Y `renovacion_automatica` = `false` |\n| C3.2 | Membresía vence en 15 días sin renovación | Media | `fecha_vencimiento` – `fecha_actual` ≤ 15 Y `renovacion_automatica` = `false` |\n| C3.3 | Membresía vence en 7 días sin renovación | Alta | `fecha_vencimiento` – `fecha_actual` ≤ 7 Y `renovacion_automatica` = `false` |\n\n### C4 — Interacción\n\n| # | Condición | Nivel | Datos a evaluar |\n|---|---|---|---|\n| C4.1 | Sin respuesta a últimos 2 recordatorios | Baja | `recordatorios_no_respondidos` ≥ 2 |\n| C4.2 | Solicitud de baja pendiente | Media | `solicitud_baja_pendiente` = `true` |\n\n### C5 — Satisfacción\n\n| # | Condición | Nivel | Datos a evaluar |\n|---|---|---|---|\n| C5.1 | Queja registrada en los últimos 30 días | Media | `quejas_ultimos_30_dias` ≥ 1 |\n| C5.2 | 2+ quejas en los últimos 60 días | Alta | `quejas_ultimos_60_dias` ≥ 2 |\n| C5.3 | Reseña negativa en plataforma pública | Alta | `reseña_negativa` = `true` |\n\n### C6 — Comportamiento\n\n| # | Condición | Nivel | Datos a evaluar |\n|---|---|---|---|\n| C6.1 | Cambio de plan a plan de menor precio | Baja | `cambio_plan_previo` = `downgrade` |\n| C6.2 | Cancelación de servicio adicional | Media | `servicios_adicionales` tiene menos elementos que en el período anterior |\n\n### C7 — Referidos\n\n| # | Condición | Nivel | Datos a evaluar |\n|---|---|---|---|\n| C7.1 | Dejó de traer referidos (si antes traía) | Baja | `referidos_previos` > 0 Y `referidos_ultimos_60_dias` = 0 |\n\n### C8 — Pago periódico\n\n| # | Condición | Nivel | Datos a evaluar |\n|---|---|---|---|\n| C8.1 | Renovación automática desactivada por el miembro | Media | `renovacion_automatica` = `false` Y `metodo_pago_activo` = `true` |\n\n---\n\n## Reglas de asignación de nivel de riesgo\n\n| Condición | Nivel asignado |\n|---|---|\n| 0 reglas cumplidas | `SIN_RIESGO` |\n| 1 regla baja solamente | `RIESGO_BAJO` |\n| 2+ reglas bajas | `RIESGO_MEDIO` |\n| 1 regla media solamente | `RIESGO_MEDIO` |\n| 2+ reglas medias | `RIESGO_ALTO` |\n| 1+ regla alta | `RIESGO_ALTO` |\n| 3+ reglas medias | `RIESGO_ALTO` |\n| 2+ reglas altas | `RIESGO_CRITICO` |\n\n---\n\n## Reglas de notificación\n\n| Nivel de riesgo | Notificar al responsable | Notificar al gerente | Plazo de seguimiento |\n|---|---|---|---|\n| `SIN_RIESGO` | No | No | N/A |\n| `RIESGO_BAJO` | No (solo registrar) | No | 7 días (monitoreo pasivo) |\n| `RIESGO_MEDIO` | Sí | No | 48 horas |\n| `RIESGO_ALTO` | Sí | Sí | 24 horas |\n| `RIESGO_CRITICO` | Sí | Sí | 12 horas |\n\n---\n\n## Acciones sugeridas por nivel de riesgo\n\n| Nivel | Acción sugerida |\n|---|---|\n| `RIESGO_BAJO` | Monitoreo pasivo. Revisar en 7 días. No requiere acción inmediata. |\n| `RIESGO_MEDIO` | Contactar al miembro para verificar su estado y ofrecer alternativas (cambio de horario, clase de prueba de otra actividad, sesión con entrenador). |\n| `RIESGO_ALTO` | Contacto urgente. Ofrecer soluciones concretas (cambio de plan, clase personalizada, descuento temporal, reparación de equipos). |\n| `RIESGO_CRITICO` | Escalar a gerencia. Contacto inmediato con propuesta de retención concreta (upgrade gratuito temporal, sesión personal gratis, cambio de plan sin costo). |\n\n---\n\n## Textos base de reglas\n\nLos siguientes textos se usan literalmente en las notificaciones. No se deben modificar ni parafrasear.\n\n| Regla | Texto de la regla |\n|---|---|\n| C1.1 | `\"Sin asistencia en los últimos {{dias_desde_ultima_asistencia}} días (última: {{ultima_fecha_asistencia}} — {{ultima_clase_asistida}})\"` |\n| C1.2 | `\"Sin asistencia en los últimos {{dias_desde_ultima_asistencia}} días (última: {{ultima_fecha_asistencia}} — {{ultima_clase_asistida}})\"` |\n| C1.3 | `\"Sin asistencia en los últimos {{dias_desde_ultima_asistencia}} días (última: {{ultima_fecha_asistencia}} — {{ultima_clase_asistida}})\"` |\n| C1.4 | `\"Asistencia promedio cayó {{porcentaje_caida}}% ({{asistencia_promedio_anterior}} → {{asistencia_promedio_actual}} veces/semana)\"` |\n| C2.1 | `\"Pago con {{dias_atraso_pago}} días de atraso (monto: ${{monto_adeudado}} {{moneda}})\"` |\n| C2.2 | `\"Pago con {{dias_atraso_pago}} días de atraso (monto: ${{monto_adeudado}} {{moneda}})\"` |\n| C2.3 | `\"Monto adeudado de ${{monto_adeudado}} {{moneda}} (excede 1 mes de membresía: ${{precio_plan}} {{moneda}})\"` |\n| C3.1 | `\"Membresía vence en {{dias_restantes}} días ({{fecha_vencimiento}}). Sin renovación automática.\"` |\n| C3.2 | `\"Membresía vence en {{dias_restantes}} días ({{fecha_vencimiento}}). Sin renovación automática.\"` |\n| C3.3 | `\"Membresía vence en {{dias_restantes}} días ({{fecha_vencimiento}}). Sin renovación automática.\"` |\n| C4.1 | `\"{{recordatorios_no_respondidos}} recordatorios enviados sin respuesta del miembro.\"` |\n| C4.2 | `\"Solicitud de baja pendiente de procesar.\"` |\n| C5.1 | `\"Queja registrada hace {{dias_desde_queja}} días: '{{queja_texto}}'\"` |\n| C5.2 | `\"{{quejas_ultimos_60_dias}} quejas en los últimos 60 días.\"` |\n| C5.3 | `\"Reseña negativa en plataforma pública: '{{reseña_negativa_texto}}'\"` |\n| C6.1 | `\"Cambio de plan registrado: downgrade de {{plan_anterior}} a {{plan_nombre}}.\"` |\n| C6.2 | `\"Cancelación de servicio adicional: {{servicio_cancelado}}.\"` |\n| C7.1 | `\"Dejó de traer referidos: {{referidos_previos}} en el pasado, 0 en los últimos 60 días.\"` |\n| C8.1 | `\"Renovación automática desactivada por el miembro.\"` |\n\n---\n\n## Resumen de tiempos de notificación\n\n| Nivel de riesgo | Tiempo antes de notificar | Notifica a gerente |\n|---|---|---|\n| `SIN_RIESGO` | No notifica | No |\n| `RIESGO_BAJO` | No notifica (solo registra) | No |\n| `RIESGO_MEDIO` | 0 segundos (inmediato) | No |\n| `RIESGO_ALTO` | 0 segundos (inmediato) | Sí |\n| `RIESGO_CRITICO` | 0 segundos (inmediato) | Sí |\n\n---\n\n## Plazos de respuesta del equipo interno\n\n| Nivel de riesgo | Plazo de respuesta esperado |\n|---|---|\n| `RIESGO_CRITICO` | 12 horas |\n| `RIESGO_ALTO` | 24 horas |\n| `RIESGO_MEDIO` | 48 horas |\n\n---\n\n## Ejemplo completo de activación\n\n**Reglas cumplidas:** C1.3 (sin actividad 30 días) + C5.1 (queja reciente) + C6.2 (canceló servicio adicional)\n\n**Nivel calculado:** `RIESGO_ALTO` (1 regla alta + 2 reglas medias)\n\n**Miembro:** Laura Hernández, plan Elite ($999/mes), sin actividad desde el 15 de julio.\n\n**Flujo del agente:**\n1. Analiza actividad: 0 asistencias en 30 días, queja hace 20 días, canceló nutrición.\n2. Detecta 3 reglas: C1.3 (alta), C5.1 (media), C6.2 (media).\n3. Calcula nivel: `RIESGO_ALTO` (score 75/100).\n4. Notifica al responsable y al gerente.\n5. Espera acción del humano.\n\n**Notificación al responsable:**\n\n> 🔴 **ALERTA DE RIESGO DE DESERCIÓN** — FitZone Premium\n> ==========================================\n>\n> **Miembro:** Laura Hernández\n> **Plan:** Elite — $999 MXN/mes\n> **Membresía:** MEM-20260301-205\n>\n> 📊 **Nivel de riesgo:** 🔴 ALTO (Score: 75/100)\n> 📋 **Reglas cumplidas:** 3 regla(s)\n>\n> 🔸 **C1.3:** Sin asistencia en los últimos 40 días (última: 15/jul — CrossFit)\n> 🔸 **C5.1:** Queja registrada hace 20 días: \"Las máquinas de cardio siempre están rotas\"\n> 🔸 **C6.2:** Cancelación de servicio adicional: nutricion\n>\n> 🎯 **Acción sugerida:** Contacto urgente. Ofrecer soluciones concretas (cambio de plan, clase personalizada, descuento temporal, reparación de equipos).\n> ⏱️ **Plazo de seguimiento:** 24 horas\n>\n> 🔗 Registro: RISK-20260824-080000-002\n\n**Registro de escalamiento a gerencia:**\n\n> 🔴 **ESCALAMIENTO A GERENCIA** — FitZone Premium\n> ==========================================\n>\n> **Miembro:** Laura Hernández (MEM-20260301-205)\n> **Nivel:** ALTO (Score: 75/100)\n> **Reglas:** C1.3 (sin actividad 40 días) + C5.1 (queja reciente) + C6.2 (canceló servicio)\n>\n> **Motivo de escalamiento:** Miembro con membresía Elite ($999/mes) sin actividad 40 días, con queja reciente sobre equipamiento y cancelación de servicio de nutrición. Alto riesgo de pérdida.\n> **Plazo de acción:** 24 horas\n>\n> El responsable Carlos Mendoza ya fue notificado.\n> 🔗 Registro: RISK-20260824-080000-002\n\n**Registro de alerta completa:**\n\n```json\n{\n  \"registro_id\": \"CRFREG-20260824-080005-002\",\n  \"riesgo_id\": \"RISK-20260824-080000-002\",\n  \"analisis_id\": \"CRF-20260824-080000-002\",\n  \"membresia_id\": \"MEM-20260301-205\",\n  \"contacto_id\": \"CON-20260301-205\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"nombre_miembro\": \"Laura Hernández\",\n  \"telefono_miembro\": \"+52 55 1122 3344\",\n  \"email_miembro\": null,\n  \"plan_nombre\": \"Elite\",\n  \"precio_plan\": 999.00,\n  \"moneda\": \"MXN\",\n  \"fecha_vencimiento\": \"2027-03-01\",\n  \"fecha_hora_recepcion\": \"2026-08-24T08:00:00\",\n  \"reglas_cumplidas\": [\n    {\"id_regla\": \"C1.3\", \"nivel_regla\": \"alta\", \"descripcion_regla\": \"Sin asistencia en los últimos 40 días\", \"datos_soporte\": {\"dias_desde_ultima_asistencia\": 40, \"ultima_fecha_asistencia\": \"2026-07-15\", \"ultima_clase_asistida\": \"CrossFit\"}},\n    {\"id_regla\": \"C5.1\", \"nivel_regla\": \"media\", \"descripcion_regla\": \"Queja registrada hace 20 días\", \"datos_soporte\": {\"queja_texto\": \"Las máquinas de cardio siempre están rotas\", \"queja_fecha\": \"2026-08-05\"}},\n    {\"id_regla\": \"C6.2\", \"nivel_regla\": \"media\", \"descripcion_regla\": \"Cancelación de servicio adicional\", \"datos_soporte\": {\"servicio_cancelado\": \"nutricion\", \"fecha_cancelacion\": \"2026-08-01\"}}\n  ],\n  \"total_reglas_bajas\": 0,\n  \"total_reglas_medias\": 2,\n  \"total_reglas_altas\": 1,\n  \"nivel_riesgo_final\": \"RIESGO_ALTO\",\n  \"regla_principal\": \"C1.3\",\n  \"score_riesgo\": 75,\n  \"fecha_hora_deteccion\": \"2026-08-24T08:00:03\",\n  \"notificacion_enviada\": true,\n  \"fecha_hora_notificacion\": \"2026-08-24T08:00:05\",\n  \"escalamiento_gerente\": true,\n  \"accion_humano\": \"envio_oferta\",\n  \"resultado_accion\": \"reintegrado\",\n  \"notas_accion\": \"Laura aceptó una clase personalizada de CrossFit con Coach Vega para el miércoles. Le ofrecimos 1 mes de nutrición gratis como compensación por las máquinas. Dijo que si la clase sale bien, continúa.\",\n  \"fecha_hora_accion\": \"2026-08-24T12:00:00\",\n  \"resultado\": \"reintegrado\",\n  \"fecha_hora_cierre\": \"2026-08-24T12:00:03\",\n  \"duracion_proceso_segundos\": 14403,\n  \"agente\": \"churn-risk-flagger\"\n}\n```\n"
              },
              {
                "id": "churn-risk-flagger/variables-entrada.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "variables-entrada.md",
                "contenido": "# Variables de entrada — Churn Risk Flagger\n\nEste archivo define la lista fija y cerrada de campos de datos que el agente necesita recibir para operar. No se admiten campos adicionales a los listados aquí.\n\n---\n\n## Campos del evento disparador\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 1 | `evento_disparador_id` | `string` | Obligatorio | Identificador único del evento disparador. Formato: `CRF-YYYYMMDD-HHMMSS-XXX`. | `\"CRF-20260824-080000-001\"` |\n| 2 | `membresia_id` | `string` | Obligatorio | Identificador de la membresía del miembro. Formato: `MEM-YYYYMMDD-XXX`. | `\"MEM-20260115-101\"` |\n| 3 | `contacto_id` | `string` | Obligatorio | Identificador del contacto asociado. Formato: `CON-YYYYMMDD-XXX`. | `\"CON-20260115-101\"` |\n| 4 | `fecha_actual` | `string` (ISO 8601: `YYYY-MM-DD`) | Obligatorio | Fecha actual del sistema para el análisis. | `\"2026-08-24\"` |\n\n---\n\n## Datos del miembro\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 5 | `nombre_miembro` | `string` | Obligatorio | Nombre completo del miembro. | `\"Ana García López\"` |\n| 6 | `telefono_miembro` | `string` | Obligatorio | Número telefónico del miembro. Entre 8 y 15 dígitos. | `\"+52 55 9876 5432\"` |\n| 7 | `email_miembro` | `string` | Opcional | Correo electrónico del miembro. | `\"ana.garcia@correo.com\"` |\n| 8 | `estado_membresia` | `string` (enum) | Obligatorio | Estado de la membresía. Solo se analizan membresías con estado `activa`. Valores: `activa`, `cancelada`, `vencida`, `suspendida`. | `\"activa\"` |\n\n---\n\n## Datos de actividad (asistencia)\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 9 | `asistencias_ultimos_30_dias` | `integer` | Obligatorio | Número de clases asistidas en los últimos 30 días. | `2` |\n| 10 | `asistencias_ultimos_7_dias` | `integer` | Obligatorio | Número de clases asistidas en los últimos 7 días. | `0` |\n| 11 | `ultima_fecha_asistencia` | `string` (ISO 8601: `YYYY-MM-DD`) | Obligatorio | Fecha de la última asistencia registrada. | `\"2026-08-10\"` |\n| 12 | `ultima_clase_asistida` | `string` | Opcional | Nombre de la última clase asistida. | `\"Spinning\"` |\n| 13 | `dias_desde_ultima_asistencia` | `integer` | Obligatorio | Días transcurridos desde la última asistencia. | `14` |\n| 14 | `asistencia_promedio_semanal_anterior` | `number` | Opcional | Promedio de asistencias por semana en el período anterior (ej. primeros 30 días). | `3.2` |\n| 15 | `asistencia_promedio_semanal_actual` | `number` | Opcional | Promedio de asistencias por semana en la última semana. | `0.5` |\n\n---\n\n## Datos de pagos\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 16 | `pagos_al_dia` | `boolean` | Obligatorio | `true` si no hay pagos atrasados. `false` si hay al menos 1 pago pendiente. | `true` |\n| 17 | `monto_adeudado` | `number` | Obligatorio | Monto total adeudado por el miembro. `0` si está al día. | `0.00` |\n| 18 | `dias_atraso_pago` | `integer` | Obligatorio | Días de atraso en el último pago pendiente. `0` si está al día. | `0` |\n| 19 | `ultima_fecha_pago` | `string` (ISO 8601: `YYYY-MM-DD`) | Obligatorio | Fecha del último pago registrado. | `\"2026-08-01\"` |\n| 20 | `metodo_pago_activo` | `boolean` | Obligatorio | `true` si el método de pago está activo y funcionando. | `true` |\n\n---\n\n## Datos de la membresía\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 21 | `plan_nombre` | `string` | Obligatorio | Nombre del plan actual. | `\"Premium\"` |\n| 22 | `precio_plan` | `number` | Obligatorio | Precio mensual del plan. | `699.00` |\n| 23 | `moneda` | `string` | Obligatorio | Código de moneda ISO 4217. | `\"MXN\"` |\n| 24 | `fecha_inicio_membresia` | `string` (ISO 8601: `YYYY-MM-DD`) | Obligatorio | Fecha de inicio de la membresía actual. | `\"2026-01-15\"` |\n| 25 | `duracion_membresia_meses` | `integer` | Obligatorio | Duración total de la membresía en meses. | `12` |\n| 26 | `renovacion_automatica` | `boolean` | Obligatorio | `true` si la renovación automática está activada. | `true` |\n| 27 | `fecha_vencimiento` | `string` (ISO 8601: `YYYY-MM-DD`) | Obligatorio | Fecha de vencimiento de la membresía. | `\"2026-12-15\"` |\n| 28 | `cambio_plan_previo` | `string` | Opcional | Último cambio de plan registrado (si aplica). Valores: `upgrade`, `downgrade`, `sin_cambio`. | `\"sin_cambio\"` |\n| 29 | `servicios_adicionales` | `array` de strings | Opcional | Lista de servicios adicionales contratados. | `[\"nutricion\", \"sauna\"]` |\n\n---\n\n## Datos de interacción y satisfacción\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 30 | `quejas_previas` | `integer` | Obligatorio | Número total de quejas registradas por el miembro. | `0` |\n| 31 | `quejas_ultimos_30_dias` | `integer` | Obligatorio | Quejas registradas en los últimos 30 días. | `0` |\n| 32 | `quejas_ultimos_60_dias` | `integer` | Obligatorio | Quejas registradas en los últimos 60 días. | `0` |\n| 33 | `recordatorios_no_respondidos` | `integer` | Obligatorio | Número de recordatorios enviados sin respuesta del miembro. | `0` |\n| 34 | `solicitud_baja_pendiente` | `boolean` | Obligatorio | `true` si el miembro solicitó baja pero aún no se procesó. | `false` |\n| 35 | `reseña_negativa` | `boolean` | Obligatorio | `true` si el miembro dejó una reseña negativa en plataforma pública. | `false` |\n| 36 | `reseña_negativa_texto` | `string` | Opcional (requerido si `reseña_negativa` = `true`) | Texto de la reseña negativa. | `\"Las máquinas siempre están rotas\"` |\n| 37 | `referidos_previos` | `integer` | Opcional | Número de referidos que el miembro trajo en el pasado. | `3` |\n| 38 | `referidos_ultimos_60_dias` | `integer` | Opcional | Referidos traídos en los últimos 60 días. | `0` |\n\n---\n\n## Datos del gimnasio (inyectados desde configuración)\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 39 | `gimnasio_id` | `string` | Obligatorio | Identificador único del gimnasio. | `\"GYM-001\"` |\n| 40 | `gimnasio_nombre` | `string` | Obligatorio | Nombre comercial del gimnasio. | `\"FitZone Premium\"` |\n| 41 | `gimnasio_telefono` | `string` | Obligatorio | Teléfono principal del gimnasio. | `\"+52 55 4321 8765\"` |\n\n---\n\n## Datos del destinatario de notificación\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 42 | `responsable_nombre` | `string` | Obligatorio | Nombre del responsable que recibe notificaciones. | `\"Carlos Mendoza\"` |\n| 43 | `responsable_telefono` | `string` | Opcional | Teléfono del responsable. | `\"+52 55 2222 3333\"` |\n| 44 | `responsable_email` | `string` | Opcional | Email del responsable. | `\"carlos.mendoza@fitzonepremium.com\"` |\n| 45 | `gerente_nombre` | `string` | Opcional (requerido si nivel es `RIESGO_ALTO` o `RIESGO_CRITICO`) | Nombre del gerente para notificaciones de alto riesgo. | `\"Luis Herrera\"` |\n| 46 | `gerente_telefono` | `string` | Opcional | Teléfono del gerente. | `\"+52 55 3333 4444\"` |\n| 47 | `gerente_email` | `string` | Opcional | Email del gerente. | `\"luis.herrera@fitzonepremium.com\"` |\n| 48 | `canal_notificacion_equipo` | `string` (enum) | Obligatorio | Canal para notificar al equipo interno. Valores: `whatsapp_interno`, `email_interno`. | `\"whatsapp_interno\"` |\n\n---\n\n## Campos calculados por el agente\n\nEstos campos **no se reciben de entrada**; el agente los genera internamente.\n\n| # | Nombre del campo | Tipo de dato | Generado en | Descripción |\n|---|---|---|---|---|\n| 49 | `analisis_id` | `string` | Paso 1 | ID único del análisis. Formato: `CRF-YYYYMMDD-HHMMSS-XXX`. |\n| 50 | `fecha_hora_recepcion` | `string` (ISO 8601) | Paso 1 | Marca de tiempo de recepción del evento. |\n| 51 | `reglas_cumplidas` | `array` de objects | Paso 2 | Lista de reglas de riesgo cumplidas con datos de soporte. |\n| 52 | `total_reglas_bajas` | `integer` | Paso 2 | Total de reglas de nivel bajo cumplidas. |\n| 53 | `total_reglas_medias` | `integer` | Paso 2 | Total de reglas de nivel medio cumplidas. |\n| 54 | `total_reglas_altas` | `integer` | Paso 2 | Total de reglas de nivel alto cumplidas. |\n| 55 | `nivel_riesgo_final` | `string` (enum) | Paso 3 | Nivel de riesgo calculado. |\n| 56 | `regla_principal` | `string` | Paso 3 | ID de la regla de mayor nivel cumplida. |\n| 57 | `riesgo_id` | `string` | Paso 3 | ID único de la alerta de riesgo. Formato: `RISK-YYYYMMDD-HHMMSS-XXX`. |\n| 58 | `score_riesgo` | `integer` | Paso 3 | Score de riesgo calculado (0–100). |\n| 59 | `fecha_hora_deteccion` | `string` (ISO 8601) | Paso 3 | Marca de tiempo de detección del riesgo. |\n| 60 | `fecha_hora_notificacion` | `string` (ISO 8601) o `null` | Paso 4 | Marca de tiempo de notificación al responsable. |\n| 61 | `notificacion_enviada` | `boolean` | Paso 4 | `true` si se envió notificación. |\n| 62 | `accion_humano` | `string` (enum) o `null` | Paso 5 | Acción realizada por el humano. |\n| 63 | `resultado_accion` | `string` (enum) o `null` | Paso 5 | Resultado de la acción. |\n| 64 | `notas_accion` | `string` o `null` | Paso 5 | Notas del humano sobre la acción. |\n| 65 | `fecha_hora_accion` | `string` (ISO 8601) o `null` | Paso 5 | Marca de tiempo de la acción. |\n| 66 | `fecha_hora_cierre` | `string` (ISO 8601) | Paso 5 | Marca de tiempo de cierre del caso. |\n| 67 | `resultado` | `string` (enum) | Paso 5 | Resultado final. |\n| 68 | `duracion_proceso_segundos` | `integer` | Paso 5 | Duración total del proceso en segundos. |\n\n---\n\n## Validaciones de entrada\n\n| Campo | Regla | Acción si falla |\n|---|---|---|\n| `evento_disparador_id` | No vacío, formato válido | Terminar flujo sin ejecutar |\n| `membresia_id` | No vacío, formato `MEM-XXXXXXXX-XXX` | Terminar flujo sin ejecutar |\n| `contacto_id` | Debe seguir formato `CON-XXXXXXXX-XXX` | Terminar flujo sin ejecutar |\n| `fecha_actual` | Formato `YYYY-MM-DD` | Usar fecha del sistema |\n| `nombre_miembro` | No vacío, mínimo 2 caracteres | Usar `\"Miembro\"` como valor por defecto |\n| `telefono_miembro` | Entre 8 y 15 dígitos | Usar dato existente o `null` |\n| `estado_membresia` | Debe ser `activa` | No analizar si no es activa |\n| `asistencias_ultimos_30_dias` | Número entero ≥ 0 | Usar `0` como valor por defecto |\n| `asistencias_ultimos_7_dias` | Número entero ≥ 0 | Usar `0` como valor por defecto |\n| `ultima_fecha_asistencia` | Formato `YYYY-MM-DD` | Usar `fecha_actual` como valor por defecto |\n| `dias_desde_ultima_asistencia` | Número entero ≥ 0 | Calcular desde `ultima_fecha_asistencia` |\n| `pagos_al_dia` | Debe ser `true` o `false` | Usar `true` como valor por defecto |\n| `monto_adeudado` | Número ≥ 0 | Usar `0` como valor por defecto |\n| `dias_atraso_pago` | Número entero ≥ 0 | Usar `0` como valor por defecto |\n| `ultima_fecha_pago` | Formato `YYYY-MM-DD` | Usar `null` como valor por defecto |\n| `metodo_pago_activo` | Debe ser `true` o `false` | Usar `true` como valor por defecto |\n| `plan_nombre` | No vacío | Terminar flujo sin ejecutar |\n| `precio_plan` | Debe ser número > 0 | Terminar flujo sin ejecutar |\n| `fecha_inicio_membresia` | Formato `YYYY-MM-DD` | Terminar flujo sin ejecutar |\n| `duracion_membresia_meses` | Número entero > 0 | Usar `12` como valor por defecto |\n| `renovacion_automatica` | Debe ser `true` o `false` | Usar `true` como valor por defecto |\n| `fecha_vencimiento` | Formato `YYYY-MM-DD` | Calcular desde `fecha_inicio_membresia` + `duracion_membresia_meses` |\n| `quejas_previas` | Número entero ≥ 0 | Usar `0` como valor por defecto |\n| `quejas_ultimos_30_dias` | Número entero ≥ 0 | Usar `0` como valor por defecto |\n| `quejas_ultimos_60_dias` | Número entero ≥ 0 | Usar `0` como valor por defecto |\n| `recordatorios_no_respondidos` | Número entero ≥ 0 | Usar `0` como valor por defecto |\n| `solicitud_baja_pendiente` | Debe ser `true` o `false` | Usar `false` como valor por defecto |\n| `reseña_negativa` | Debe ser `true` o `false` | Usar `false` como valor por defecto |\n| `gimnasio_id` | No vacío | Terminar flujo sin ejecutar |\n| `gimnasio_nombre` | No vacío | Terminar flujo sin ejecutar |\n| `responsable_nombre` | No vacío | Usar `\"Coordinador de Retención\"` como valor por defecto |\n| `canal_notificacion_equipo` | Debe ser uno de los 2 valores válidos | Usar `\"whatsapp_interno\"` como valor por defecto |\n"
              }
            ]
          }
        ]
      },
      {
        "id": "personal-training-booking",
        "carpeta": "personal-training-booking-agent",
        "nombre": "Personal Training Booking Agent",
        "icono": "💪",
        "descripcion": "Gestionar reservas de sesiones de entrenamiento personal",
        "submodulos": [],
        "categoria": "general",
        "estructura": [
          {
            "id": "personal-training-booking-agent",
            "etiqueta": "",
            "es_plantilla": true,
            "nombre": "personal-training-booking-agent",
            "subcarpetas": [],
            "notas": [
              {
                "id": "personal-training-booking-agent/flujo.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "flujo.md",
                "contenido": "# Flujo de ejecución — Personal Training Booking Agent\n\nEste archivo documenta el flujo fijo de ejecución del agente. Cada solicitud de reserva de entrenamiento personal sigue estos 6 pasos **en orden**, sin pasos condicionales ni bifurcaciones.\n\n---\n\n## Paso 1 — Recepción de la solicitud de cita/reserva con fecha y servicio deseado\n\nEl agente recibe la solicitud de una sesión de entrenamiento personal de un contacto (prospecto nuevo o miembro actual) a través de cualquier canal configurado.\n\n**Acción del agente:**\n1. Recibir el evento disparador `solicitud_reserva`.\n2. Extraer los campos de entrada definidos en `variables-entrada.md`.\n3. Registrar la marca de tiempo de recepción en `fecha_hora_recepcion`.\n4. Generar un `solicitud_id` único para el caso.\n5. Verificar que la solicitud contenga al menos un tipo de sesión deseado (`tipo_sesion_solicitada`).\n6. Extraer la `fecha_preferida` y `hora_preferida` si el usuario las indicó en su mensaje.\n7. Extraer el `entrenador_preferido` si el usuario lo indicó.\n8. Extraer el `objetivo_entrenamiento` si el usuario lo indicó (ej. \"ganar masa muscular\", \"bajar de peso\", \"mejorar resistencia\").\n\n**Ejemplo de entrada:**\n\n```json\n{\n  \"evento\": \"solicitud_reserva\",\n  \"canal_origen\": \"whatsapp\",\n  \"numero_remitente\": \"+52 55 9876 5432\",\n  \"mensaje\": \"Hola, quiero agendar una sesión de entrenamiento personal de fuerza para el lunes por la mañana, con el coach Martínez si es posible\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"gimnasio_telefono\": \"+52 55 4321 8765\"\n}\n```\n\n---\n\n## Paso 2 — Consulta de disponibilidad en el calendario conectado\n\nEl agente consulta el sistema de calendario del gimnasio para verificar la disponibilidad de horarios según el tipo de sesión, la fecha/hora preferida y el entrenador preferido.\n\n**Acción del agente:**\n1. Clasificar el `tipo_sesion_solicitada` según la tabla de tipos de sesión de `prompt-sistema.md`.\n2. Determinar la `fecha_preferida` solicitada (si el usuario la indicó). Si no la indicó, usar la fecha actual o el siguiente día hábil.\n3. Determinar la `hora_preferida` solicitada (si el usuario la indicó). Si no la indicó, usar cualquier horario disponible dentro del horario de atención.\n4. Consultar la agenda del `entrenador_preferido` (si se especificó) o de todos los entrenadores disponibles.\n5. Consultar el calendario conectado para obtener los horarios disponibles en los próximos **7 días naturales** a partir de la `fecha_preferida`.\n6. Filtrar los resultados excluyendo:\n   - Horarios fuera del horario de atención (`horario_atencion_inicio` – `horario_atencion_fin`).\n   - Horarios donde el entrenador no esté disponible.\n   - Horarios que se superpongan con sesiones ya agendadas.\n   - Horarios con menos de 30 minutos de separación de otra sesión del mismo entrenador.\n7. Seleccionar los **3 mejores horarios** para ofrecer al usuario, priorizando:\n   - Horarios más cercanos a la `fecha_preferida` y `hora_preferida`.\n   - Horarios con el entrenador preferido (si se especificó).\n   - Horarios dentro del mismo día de la semana si fue solicitado.\n8. Si se especificó un entrenador preferido y no tiene disponibilidad, ofrecer alternativas con otros entrenadores.\n\n**Ejemplo de consulta al calendario:**\n\n```json\n{\n  \"solicitud_id\": \"PTB-20260823-103000-001\",\n  \"tipo_sesion_solicitada\": \"FUERZA\",\n  \"entrenador_preferido\": \"Coach Martínez\",\n  \"fecha_preferida\": \"2026-08-25\",\n  \"hora_preferida\": \"07:00\",\n  \"resultado_consulta\": {\n    \"entrenadores_disponibles\": [\"Coach Martínez\", \"Coach Herrera\", \"Coach Díaz\"],\n    \"entrenador_preferido_disponible\": true,\n    \"horarios_encontrados\": 6,\n    \"horarios_disponibles\": [\n      {\"fecha\": \"2026-08-25\", \"hora_inicio\": \"07:00\", \"hora_fin\": \"08:00\", \"entrenador\": \"Coach Martínez\", \"tipo_sesion\": \"FUERZA\", \"disponible\": true},\n      {\"fecha\": \"2026-08-25\", \"hora_inicio\": \"16:00\", \"hora_fin\": \"17:00\", \"entrenador\": \"Coach Herrera\", \"tipo_sesion\": \"FUNCIONAL\", \"disponible\": true},\n      {\"fecha\": \"2026-08-27\", \"hora_inicio\": \"07:00\", \"hora_fin\": \"08:00\", \"entrenador\": \"Coach Martínez\", \"tipo_sesion\": \"FUERZA\", \"disponible\": true},\n      {\"fecha\": \"2026-08-29\", \"hora_inicio\": \"07:00\", \"hora_fin\": \"08:00\", \"entrenador\": \"Coach Martínez\", \"tipo_sesion\": \"FUERZA\", \"disponible\": true},\n      {\"fecha\": \"2026-08-29\", \"hora_inicio\": \"18:00\", \"hora_fin\": \"19:00\", \"entrenador\": \"Coach Díaz\", \"tipo_sesion\": \"CARDIO\", \"disponible\": true},\n      {\"fecha\": \"2026-08-25\", \"hora_inicio\": \"10:00\", \"hora_fin\": \"11:00\", \"entrenador\": \"Coach Martínez\", \"tipo_sesion\": \"FUERZA\", \"disponible\": false, \"razon\": \"sesion_y_agendada\"}\n    ],\n    \"horarios_seleccionados_para_ofrecer\": [\n      {\"fecha\": \"2026-08-25\", \"hora_inicio\": \"07:00\", \"hora_fin\": \"08:00\", \"entrenador\": \"Coach Martínez\", \"tipo_sesion\": \"FUERZA\"},\n      {\"fecha\": \"2026-08-25\", \"hora_inicio\": \"16:00\", \"hora_fin\": \"17:00\", \"entrenador\": \"Coach Herrera\", \"tipo_sesion\": \"FUNCIONAL\"},\n      {\"fecha\": \"2026-08-27\", \"hora_inicio\": \"07:00\", \"hora_fin\": \"08:00\", \"entrenador\": \"Coach Martínez\", \"tipo_sesion\": \"FUERZA\"}\n    ]\n  }\n}\n```\n\n---\n\n## Paso 3 — Ofrecimiento de hasta 3 horarios disponibles concretos\n\nEl agente genera y envía al usuario un mensaje con las opciones de horario disponibles para su sesión de entrenamiento personal.\n\n**Acción del agente:**\n1. Seleccionar los 3 horarios del `horarios_seleccionados_para_ofrecer` del paso 2.\n2. Formatear cada opción con fecha completa (día de semana + día + mes + año), hora de inicio y fin, nombre del entrenador y tipo de sesión.\n3. Generar el mensaje de ofrecimiento usando la plantilla de `formato-salida.md` → sección 1.\n4. Enviar el mensaje por el `canal_origen`.\n5. Si el envío falla, reintentar una sola vez después de 30 segundos.\n6. Si el segundo intento falla, registrar el error y activar escalamiento (ver `reglas-escalamiento.md` → C3.1).\n7. Registrar la marca de tiempo de envío en `fecha_hora_envio_oferta`.\n8. Actualizar `estado_reserva` a `oferta_enviada`.\n9. Iniciar el contador de plazo de respuesta: **24 horas** desde el envío de la oferta.\n10. Calcular el `momento_expiracion_oferta`: `fecha_hora_envio_oferta` + 24 horas.\n\n**Plantilla de ofrecimiento de horarios (whatsapp/sms):**\n\n```\n¡Hola {{nombre_contacto}}! 💪 Consulté la disponibilidad de entrenamiento personal y estas son las opciones:\n\n📅 **Opción 1:** {{dia_semana_op1}} {{fecha_op1_formateada}} a las {{hora_inicio_op1}} — {{tipo_sesion_op1}} con {{entrenador_op1}}\n📅 **Opción 2:** {{dia_semana_op2}} {{fecha_op2_formateada}} a las {{hora_inicio_op2}} — {{tipo_sesion_op2}} con {{entrenador_op2}}\n📅 **Opción 3:** {{dia_semana_op3}} {{fecha_op3_formateada}} a las {{hora_inicio_op3}} — {{tipo_sesion_op3}} con {{entrenador_op3}}\n\n¿Cuál te conviene más? Responde con el número (1, 2 o 3).\n— {{gimnasio_nombre}}\n```\n\n### Ejemplo renderizado (whatsapp)\n\n> ¡Hola Ana García López! 💪 Consulté la disponibilidad de entrenamiento personal y estas son las opciones:\n>\n> 📅 **Opción 1:** Lunes 25 de agosto de 2026 a las 07:00 — Fuerza con Coach Martínez\n> 📅 **Opción 2:** Lunes 25 de agosto de 2026 a las 16:00 — Funcional con Coach Herrera\n> 📅 **Opción 3:** Miércoles 27 de agosto de 2026 a las 07:00 — Fuerza con Coach Martínez\n>\n> ¿Cuál te conviene más? Responde con el número (1, 2 o 3).\n> — FitZone Premium\n\n---\n\n## Paso 4 — Confirmación de la selección del usuario y creación del evento en el calendario\n\nEl agente recibe la selección del usuario, verifica la disponibilidad del horario seleccionado y crea el evento en el calendario del gimnasio.\n\n**Acción del agente:**\n1. Recibir la respuesta del usuario con su selección (número de opción: `1`, `2` o `3`).\n2. Validar que la selección sea uno de los 3 números válidos:\n   - Si es válido → proceder con la reserva.\n   - Si no es válido → enviar mensaje pidiendo que responda con 1, 2 o 3.\n3. Verificar que el horario seleccionado **aún esté disponible** en el calendario (puede haber cambiado desde el paso 2):\n   - Si está disponible → proceder a crear el evento.\n   - Si ya no está disponible → informar al usuario y ofrecer las opciones restantes.\n4. Crear el evento en el calendario conectado con los siguientes datos:\n   - `evento_calendario_id`: ID del evento creado en el sistema.\n   - `fecha_evento`: fecha de la reserva.\n   - `hora_inicio`: hora de inicio.\n   - `hora_fin`: hora de fin calculada = `hora_inicio` + `duracion_tipo_sesion` minutos.\n   - `tipo_sesion`: tipo de sesión seleccionada.\n   - `entrenador_nombre`: nombre del entrenador asignado.\n   - `contacto_id`: ID del contacto.\n   - `estado`: `confirmada`.\n5. Generar el `reserva_id` único para esta reserva. Formato: `PTB-YYYYMMDD-HHMMSS-XXX`.\n6. Registrar la marca de tiempo de confirmación en `fecha_hora_confirmacion`.\n7. Actualizar `estado_reserva` de `oferta_enviada` a `confirmada`.\n\n**Tabla de duración por tipo de sesión:**\n\n| Tipo de sesión | Duración (min) | Ejemplo hora_fin |\n|---|---|---|\n| `FUERZA` | 60 | 07:00 → 08:00 |\n| `FUNCIONAL` | 60 | 07:00 → 08:00 |\n| `CARDIO` | 45 | 07:00 → 07:45 |\n| `FLEXIBILIDAD` | 45 | 07:00 → 07:45 |\n| `REHABILITACION` | 60 | 07:00 → 08:00 |\n| `EVALUACION` | 45 | 07:00 → 07:45 |\n\n---\n\n## Paso 5 — Envío de confirmación por escrito al usuario con fecha, hora y detalles\n\nEl agente envía al usuario un mensaje de confirmación completo con todos los datos de la reserva.\n\n**Acción del agente:**\n1. Generar el mensaje de confirmación usando la plantilla de `formato-salida.md` → sección 2.\n2. Incluir en el mensaje:\n   - Código de sesión (`reserva_id`).\n   - Fecha completa formateada (día de semana + día + mes + año).\n   - Hora de inicio y fin.\n   - Tipo de sesión.\n   - Nombre del entrenador.\n   - Dirección o sala del gimnasio.\n   - Precio de la sesión.\n   - Tips para la sesión.\n3. Enviar el mensaje por el `canal_origen`.\n4. Si el envío falla, reintentar una sola vez después de 30 segundos.\n5. Si el segundo intento falla, registrar el error y activar escalamiento (ver `reglas-escalamiento.md` → C3.2).\n6. Registrar la marca de tiempo de envío en `fecha_hora_envio_confirmacion`.\n7. Actualizar `estado_envio_confirmacion` a `enviada`.\n\n---\n\n## Paso 6 — Registro del nuevo contacto o actualización del existente en la base de datos\n\nEl agente registra o actualiza la información del contacto en la base de datos del gimnasio.\n\n**Acción del agente:**\n1. Verificar si el `contacto_id` ya existe en la base de datos:\n   - Si existe → actualizar los campos necesarios (última interacción, historial de sesiones).\n   - Si no existe → crear un nuevo registro de contacto.\n2. Para un **contacto nuevo**, crear el registro con:\n   - `contacto_id`: ID proporcionado o generado.\n   - `nombre`: nombre del contacto.\n   - `telefono`: número telefónico.\n   - `email`: correo electrónico (si está disponible).\n   - `fuente_contacto`: `\"entrenamiento_personal\"`.\n   - `fecha_primer_contacto`: marca de tiempo actual.\n   - `ultima_interaccion`: marca de tiempo actual.\n   - `sesiones`: array con la reserva recién creada.\n   - `estado_contacto`: `\"prospecto\"`.\n3. Para un **contacto existente**, actualizar:\n   - `ultima_interaccion`: marca de tiempo actual.\n   - `sesiones`: agregar la nueva reserva al array existente.\n4. Generar el registro completo de la reserva con el formato de `formato-salida.md` → sección 4 (registro JSON).\n5. Registrar la marca de tiempo de cierre en `fecha_hora_cierre`.\n6. Calcular `duracion_proceso_segundos` = `fecha_hora_cierre` - `fecha_hora_recepcion`.\n7. Actualizar `estado_reserva` a `completada`.\n8. Guardar el registro en el sistema de historial de contactos.\n\n**Tabla de resultados posibles:**\n\n| Resultado | Descripción | Próxima acción del sistema |\n|---|---|---|\n| `reserva_confirmada` | Se creó la reserva exitosamente en el calendario | Enviar recordatorio 24h antes. Seguimiento post-sesión en 24h. |\n| `contacto_nuevo_creado` | Se creó un nuevo registro de contacto | Agregar a lista de nurturing de prospectos. |\n| `contacto_actualizado` | Se actualizó el registro de contacto existente | Actualizar perfil en CRM. |\n| `escalamiento` | Se escaló a humano por condición de `reglas-escalamiento.md` | Esperar resolución del humano. Plazo según categoría. |\n| `oferta_expirada` | El usuario no respondió en 24 horas | Cerrar caso. No se envía más mensajes. |\n| `envio_fallido` | No se pudo entregar la confirmación al contacto | Escalar a humano (C3.2). Registro de error guardado. |\n"
              },
              {
                "id": "personal-training-booking-agent/formato-salida.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "formato-salida.md",
                "contenido": "# Formato de salida — Personal Training Booking Agent\n\nEste archivo define la estructura exacta de los mensajes y registros que el agente debe producir. No se permite desviarse de estas plantillas.\n\n---\n\n## 1. Ofrecimiento de horarios disponibles — Mensaje al contacto\n\nSe genera en el paso 3 del flujo. Se envía por el canal configurado (`canal_origen`).\n\n### Plantilla — Ofrecimiento de horarios (whatsapp/sms)\n\n```\n¡Hola {{nombre_contacto}}! 💪 Consulté la disponibilidad de entrenamiento personal y estas son las opciones:\n\n📅 **Opción 1:** {{dia_semana_op1}} {{fecha_op1_formateada}} a las {{hora_inicio_op1}} — {{tipo_sesion_op1}} con {{entrenador_op1}}\n📅 **Opción 2:** {{dia_semana_op2}} {{fecha_op2_formateada}} a las {{hora_inicio_op2}} — {{tipo_sesion_op2}} con {{entrenador_op2}}\n📅 **Opción 3:** {{dia_semana_op3}} {{fecha_op3_formateada}} a las {{hora_inicio_op3}} — {{tipo_sesion_op3}} con {{entrenador_op3}}\n\n¿Cuál te conviene más? Responde con el número (1, 2 o 3).\n— {{gimnasio_nombre}}\n```\n\n### Plantilla — Ofrecimiento de horarios (email)\n\n**Asunto:** Entrenamiento personal disponible — {{gimnasio_nombre}}\n\n```\nHola {{nombre_contacto}},\n\nConsultamos la disponibilidad de entrenamiento personal en {{gimnasio_nombre}}. Estas son las opciones:\n\n1. {{dia_semana_op1}} {{fecha_op1_formateada}} a las {{hora_inicio_op1}} — {{tipo_sesion_op1}} con {{entrenador_op1}}\n2. {{dia_semana_op2}} {{fecha_op2_formateada}} a las {{hora_inicio_op2}} — {{tipo_sesion_op2}} con {{entrenador_op2}}\n3. {{dia_semana_op3}} {{fecha_op3_formateada}} a las {{hora_inicio_op3}} — {{tipo_sesion_op3}} con {{entrenador_op3}}\n\nResponde a este correo con el número de tu opción preferida (1, 2 o 3).\n\n¡Te esperamos! 💪\n— {{gimnasio_nombre}}\n```\n\n### Ejemplo renderizado (whatsapp)\n\n> ¡Hola Ana García López! 💪 Consulté la disponibilidad de entrenamiento personal y estas son las opciones:\n>\n> 📅 **Opción 1:** Lunes 25 de agosto de 2026 a las 07:00 — Fuerza con Coach Martínez\n> 📅 **Opción 2:** Lunes 25 de agosto de 2026 a las 16:00 — Funcional con Coach Herrera\n> 📅 **Opción 3:** Miércoles 27 de agosto de 2026 a las 07:00 — Fuerza con Coach Martínez\n>\n> ¿Cuál te conviene más? Responde con el número (1, 2 o 3).\n> — FitZone Premium\n\n---\n\n## 2. Confirmación de reserva — Mensaje al contacto\n\nSe genera en el paso 5 del flujo. Se envía después de que el usuario selecciona un horario y se crea el evento en el calendario.\n\n### Plantilla — Confirmación de reserva (whatsapp/sms)\n\n```\n¡Sesión confirmada! 🎉\n\n📅 **Fecha:** {{dia_semana_reserva}} {{fecha_reserva_formateada}}\n🕐 **Hora:** {{hora_inicio}}–{{hora_fin}}\n🏋️ **Tipo:** {{tipo_sesion_nombre}}\n👨‍🏫 **Entrenador:** {{entrenador_nombre}}\n📍 **Lugar:** {{gimnasio_nombre}} — {{sala_entrenamiento_personal}}\n💰 **Precio:** {{texto_precio}}\n\nTu código de sesión es **{{reserva_id}}**. Guárdalo para futuras consultas.\n\n💡 **Tips para tu sesión:**\n• Llega 10 minutos antes para calentar.\n• Trae ropa deportiva cómoda y tenis de entrenamiento.\n• Trae una botella de agua y una toalla pequeña.\n• Si tienes alguna lesión o limitación, avísale a tu entrenador al inicio.\n\n{{texto_recordatorio}}\n\n¡Nos vemos en la sala! 💪\n— {{gimnasio_nombre}}\n```\n\n### Plantilla — Confirmación de reserva (email)\n\n**Asunto:** Sesión confirmada — {{tipo_sesion_nombre}} {{fecha_reserva_formateada}} — {{gimnasio_nombre}}\n\n```\nHola {{nombre_contacto}},\n\nTu sesión de entrenamiento personal ha sido confirmada en {{gimnasio_nombre}}.\n\n📅 **Fecha:** {{dia_semana_reserva}} {{fecha_reserva_formateada}}\n🕐 **Hora:** {{hora_inicio}}–{{hora_fin}}\n🏋️ **Tipo:** {{tipo_sesion_nombre}}\n👨‍🏫 **Entrenador:** {{entrenador_nombre}}\n📍 **Lugar:** {{sala_entrenamiento_personal}} — {{gimnasio_direccion}}\n💰 **Precio:** {{texto_precio}}\n\n**Código de sesión:** {{reserva_id}}\n\n💡 **Tips para tu sesión:**\n• Llega 10 minutos antes para calentar.\n• Trae ropa deportiva cómoda y tenis de entrenamiento.\n• Trae una botella de agua y una toalla pequeña.\n• Si tienes alguna lesión o limitación, avísale a tu entrenador al inicio.\n\nTe enviaremos un recordatorio 24 horas antes de tu sesión.\n\n¡Te esperamos! 💪\n— {{gimnasio_nombre}}\n```\n\n### Ejemplo renderizado (whatsapp)\n\n> ¡Sesión confirmada! 🎉\n>\n> 📅 **Fecha:** Lunes 25 de agosto de 2026\n> 🕐 **Hora:** 07:00–08:00\n> 🏋️ **Tipo:** Fuerza\n> 👨‍🏫 **Entrenador:** Coach Martínez\n> 📍 **Lugar:** FitZone Premium — Sala PT — Planta Alta\n> 💰 **Precio:** GRATIS (incluido en plan Elite — 2 sesiones restantes)\n>\n> Tu código de sesión es **PTB-20260823-103000-001**. Guárdalo para futuras consultas.\n>\n> 💡 **Tips para tu sesión:**\n> • Llega 10 minutos antes para calentar.\n> • Trae ropa deportiva cómoda y tenis de entrenamiento.\n> • Trae una botella de agua y una toalla pequeña.\n> • Si tienes alguna lesión o limitación, avísale a tu entrenador al inicio.\n>\n> 📲 Te enviaremos un recordatorio 24 horas antes de tu sesión.\n>\n> ¡Nos vemos en la sala! 💪\n> — FitZone Premium\n\n---\n\n## 3. Notificación de reserva al equipo interno\n\n### Plantilla (whatsapp_interno)\n\n```\n📋 NUEVA RESERVA DE ENTRENAMIENTO PERSONAL — {{gimnasio_nombre}}\n==========================================\n\n**Contacto:** {{nombre_contacto}}\n**Teléfono:** {{telefono_contacto}}\n**Canal:** {{canal_origen}}\n**Miembro:** {{texto_miembro}}\n\n🏋️ **Tipo de sesión:** {{tipo_sesion_nombre}}\n📅 **Fecha:** {{dia_semana_reserva}} {{fecha_reserva_formateada}}\n🕐 **Hora:** {{hora_inicio}}–{{hora_fin}}\n👨‍🏫 **Entrenador:** {{entrenador_nombre}}\n💰 **Precio:** {{texto_precio}}\n\n📋 **Objetivo:** {{objetivo_entrenamiento}}\n⚠️ **Lesiones/limitaciones:** {{lesiones_limitaciones}}\n\n🔗 Registro: {{reserva_id}}\n🔗 Evento calendario: {{evento_calendario_id}}\n```\n\n### Ejemplo renderizado (whatsapp_interno)\n\n> 📋 NUEVA RESERVA DE ENTRENAMIENTO PERSONAL — FitZone Premium\n> ==========================================\n>\n> **Contacto:** Ana García López\n> **Teléfono:** +52 55 9876 5432\n> **Canal:** whatsapp\n> **Miembro:** Sí — Plan Elite (2 sesiones restantes)\n>\n> 🏋️ **Tipo de sesión:** Fuerza\n> 📅 **Fecha:** Lunes 25 de agosto de 2026\n> 🕐 **Hora:** 07:00–08:00\n> 👨‍🏫 **Entrenador:** Coach Martínez\n> 💰 **Precio:** GRATIS (incluido en plan Elite)\n>\n> 📋 **Objetivo:** Ganar masa muscular\n> ⚠️ **Lesiones/limitaciones:** Sin lesiones conocidas\n>\n> 🔗 Registro: PTB-20260823-103000-001\n> 🔗 Evento calendario: EVT-20260823-103045-501\n\n---\n\n## 4. Registro de reserva completa (JSON)\n\nSe genera al finalizar el paso 6 del flujo para cada reserva procesada.\n\n```json\n{\n  \"registro_id\": \"PTBREG-YYYYMMDD-HHMMSS-XXX\",\n  \"solicitud_id\": \"PTB-YYYYMMDD-HHMMSS-XXX\",\n  \"reserva_id\": \"PTB-YYYYMMDD-HHMMSS-XXX\",\n  \"evento_calendario_id\": \"EVT-YYYYMMDD-HHMMSS-XXX | null\",\n  \"contacto_id\": \"CON-YYYYMMDD-XXX\",\n  \"gimnasio_id\": \"string\",\n  \"gimnasio_nombre\": \"string\",\n  \"nombre_contacto\": \"string\",\n  \"telefono_contacto\": \"string\",\n  \"email_contacto\": \"string | null\",\n  \"canal_origen\": \"whatsapp | sms | email | app | web\",\n  \"es_miembro_actual\": true | false,\n  \"membresia_id\": \"string | null\",\n  \"plan_actual\": \"basico | premium | elite | null\",\n  \"sesiones_personal_incluidas\": 0 | null,\n  \"sesiones_personal_consumidas\": 0 | null,\n  \"tipo_sesion_solicitada\": \"FUERZA | FUNCIONAL | CARDIO | FLEXIBILIDAD | REHABILITACION | EVALUACION\",\n  \"entrenador_preferido\": \"string | null\",\n  \"fecha_preferida\": \"YYYY-MM-DD | null\",\n  \"hora_preferida\": \"HH:MM | null\",\n  \"objetivo_entrenamiento\": \"string | null\",\n  \"notas_especiales\": \"string | null\",\n  \"lesiones_limitaciones\": \"string | null\",\n  \"mensaje_solicitud\": \"string\",\n  \"fecha_hora_recepcion\": \"YYYY-MM-DDTHH:MM:SS\",\n  \"horarios_ofrecidos\": [\n    {\"fecha\": \"YYYY-MM-DD\", \"hora_inicio\": \"HH:MM\", \"hora_fin\": \"HH:MM\", \"entrenador\": \"string\", \"tipo_sesion\": \"string\"}\n  ],\n  \"seleccion_usuario\": 1 | 2 | 3 | null,\n  \"fecha_hora_envio_oferta\": \"YYYY-MM-DDTHH:MM:SS | null\",\n  \"momento_expiracion_oferta\": \"YYYY-MM-DDTHH:MM:SS | null\",\n  \"fecha_hora_confirmacion\": \"YYYY-MM-DDTHH:MM:SS | null\",\n  \"fecha_reserva\": \"YYYY-MM-DD | null\",\n  \"hora_inicio\": \"HH:MM | null\",\n  \"hora_fin\": \"HH:MM | null\",\n  \"duracion_minutos\": 0 | null,\n  \"tipo_sesion_nombre\": \"string | null\",\n  \"entrenador_nombre\": \"string | null\",\n  \"precio_sesion\": 0.00,\n  \"moneda\": \"string\",\n  \"precio_incluido_plan\": true | false,\n  \"sesiones_restantes\": 0 | null,\n  \"fecha_hora_envio_confirmacion\": \"YYYY-MM-DDTHH:MM:SS | null\",\n  \"estado_envio_confirmacion\": \"generada | enviada | error_envio\",\n  \"contacto_es_nuevo\": true | false,\n  \"estado_reserva\": \"oferta_enviada | confirmada | completada | cancelada | no_show | escalamiento\",\n  \"resultado\": \"reserva_confirmada | escalamiento | oferta_expirada | envio_fallido\",\n  \"fue_escalado\": true | false,\n  \"motivo_escalamiento\": \"string | null\",\n  \"fecha_hora_cierre\": \"YYYY-MM-DDTHH:MM:SS\",\n  \"duracion_proceso_segundos\": 0,\n  \"agente\": \"personal-training-booking-agent\"\n}\n```\n\n### Ejemplo — Reserva confirmada (miembro con sesiones incluidas)\n\n```json\n{\n  \"registro_id\": \"PTBREG-20260823-103100-001\",\n  \"solicitud_id\": \"PTB-20260823-103000-001\",\n  \"reserva_id\": \"PTB-20260823-103000-001\",\n  \"evento_calendario_id\": \"EVT-20260823-103045-501\",\n  \"contacto_id\": \"CON-20260823-201\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"nombre_contacto\": \"Ana García López\",\n  \"telefono_contacto\": \"+52 55 9876 5432\",\n  \"email_contacto\": \"ana.garcia@correo.com\",\n  \"canal_origen\": \"whatsapp\",\n  \"es_miembro_actual\": true,\n  \"membresia_id\": \"MEM-20260115-101\",\n  \"plan_actual\": \"elite\",\n  \"sesiones_personal_incluidas\": 4,\n  \"sesiones_personal_consumidas\": 2,\n  \"tipo_sesion_solicitada\": \"FUERZA\",\n  \"entrenador_preferido\": \"Coach Martínez\",\n  \"fecha_preferida\": \"2026-08-25\",\n  \"hora_preferida\": \"07:00\",\n  \"objetivo_entrenamiento\": \"ganar masa muscular\",\n  \"notas_especiales\": \"Soy principiante, nunca he entrenado con pesas\",\n  \"lesiones_limitaciones\": null,\n  \"mensaje_solicitud\": \"Hola, quiero agendar una sesión de entrenamiento personal de fuerza para el lunes por la mañana, con el coach Martínez si es posible\",\n  \"fecha_hora_recepcion\": \"2026-08-23T10:30:00\",\n  \"horarios_ofrecidos\": [\n    {\"fecha\": \"2026-08-25\", \"hora_inicio\": \"07:00\", \"hora_fin\": \"08:00\", \"entrenador\": \"Coach Martínez\", \"tipo_sesion\": \"FUERZA\"},\n    {\"fecha\": \"2026-08-25\", \"hora_inicio\": \"16:00\", \"hora_fin\": \"17:00\", \"entrenador\": \"Coach Herrera\", \"tipo_sesion\": \"FUNCIONAL\"},\n    {\"fecha\": \"2026-08-27\", \"hora_inicio\": \"07:00\", \"hora_fin\": \"08:00\", \"entrenador\": \"Coach Martínez\", \"tipo_sesion\": \"FUERZA\"}\n  ],\n  \"seleccion_usuario\": 1,\n  \"fecha_hora_envio_oferta\": \"2026-08-23T10:30:03\",\n  \"momento_expiracion_oferta\": \"2026-08-24T10:30:03\",\n  \"fecha_hora_confirmacion\": \"2026-08-23T10:35:00\",\n  \"fecha_reserva\": \"2026-08-25\",\n  \"hora_inicio\": \"07:00\",\n  \"hora_fin\": \"08:00\",\n  \"duracion_minutos\": 60,\n  \"tipo_sesion_nombre\": \"Fuerza\",\n  \"entrenador_nombre\": \"Coach Martínez\",\n  \"precio_sesion\": 0.00,\n  \"moneda\": \"MXN\",\n  \"precio_incluido_plan\": true,\n  \"sesiones_restantes\": 1,\n  \"fecha_hora_envio_confirmacion\": \"2026-08-23T10:35:03\",\n  \"estado_envio_confirmacion\": \"enviada\",\n  \"contacto_es_nuevo\": false,\n  \"estado_reserva\": \"completada\",\n  \"resultado\": \"reserva_confirmada\",\n  \"fue_escalado\": false,\n  \"motivo_escalamiento\": null,\n  \"fecha_hora_cierre\": \"2026-08-23T10:35:05\",\n  \"duracion_proceso_segundos\": 305,\n  \"agente\": \"personal-training-booking-agent\"\n}\n```\n\n### Ejemplo — Prospecto reserva evaluación inicial\n\n```json\n{\n  \"registro_id\": \"PTBREG-20260823-150000-002\",\n  \"solicitud_id\": \"PTB-20260823-150000-002\",\n  \"reserva_id\": \"PTB-20260823-150000-002\",\n  \"evento_calendario_id\": \"EVT-20260823-150030-502\",\n  \"contacto_id\": \"CON-20260823-305\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"nombre_contacto\": \"Roberto Ruiz Méndez\",\n  \"telefono_contacto\": \"+52 55 4567 8901\",\n  \"email_contacto\": null,\n  \"canal_origen\": \"whatsapp\",\n  \"es_miembro_actual\": false,\n  \"membresia_id\": null,\n  \"plan_actual\": null,\n  \"sesiones_personal_incluidas\": null,\n  \"sesiones_personal_consumidas\": null,\n  \"tipo_sesion_solicitada\": \"EVALUACION\",\n  \"entrenador_preferido\": null,\n  \"fecha_preferida\": \"2026-08-26\",\n  \"hora_preferida\": \"10:00\",\n  \"objetivo_entrenamiento\": \"bajar de peso\",\n  \"notas_especiales\": null,\n  \"lesiones_limitaciones\": \"Dolor lumbar crónico\",\n  \"mensaje_solicitud\": \"Quiero hacer una evaluación física para empezar a entrenar, quiero bajar de peso\",\n  \"fecha_hora_recepcion\": \"2026-08-23T15:00:00\",\n  \"horarios_ofrecidos\": [\n    {\"fecha\": \"2026-08-24\", \"hora_inicio\": \"10:00\", \"hora_fin\": \"10:45\", \"entrenador\": \"Coach Díaz\", \"tipo_sesion\": \"EVALUACION\"},\n    {\"fecha\": \"2026-08-24\", \"hora_inicio\": \"16:00\", \"hora_fin\": \"16:45\", \"entrenador\": \"Coach Díaz\", \"tipo_sesion\": \"EVALUACION\"},\n    {\"fecha\": \"2026-08-25\", \"hora_inicio\": \"10:00\", \"hora_fin\": \"10:45\", \"entrenador\": \"Coach Díaz\", \"tipo_sesion\": \"EVALUACION\"}\n  ],\n  \"seleccion_usuario\": 1,\n  \"fecha_hora_envio_oferta\": \"2026-08-23T15:00:03\",\n  \"momento_expiracion_oferta\": \"2026-08-24T15:00:03\",\n  \"fecha_hora_confirmacion\": \"2026-08-23T15:05:00\",\n  \"fecha_reserva\": \"2026-08-24\",\n  \"hora_inicio\": \"10:00\",\n  \"hora_fin\": \"10:45\",\n  \"duracion_minutos\": 45,\n  \"tipo_sesion_nombre\": \"Evaluación\",\n  \"entrenador_nombre\": \"Coach Díaz\",\n  \"precio_sesion\": 0.00,\n  \"moneda\": \"MXN\",\n  \"precio_incluido_plan\": false,\n  \"sesiones_restantes\": null,\n  \"fecha_hora_envio_confirmacion\": \"2026-08-23T15:05:02\",\n  \"estado_envio_confirmacion\": \"enviada\",\n  \"contacto_es_nuevo\": true,\n  \"estado_reserva\": \"completada\",\n  \"resultado\": \"reserva_confirmada\",\n  \"fue_escalado\": false,\n  \"motivo_escalamiento\": null,\n  \"fecha_hora_cierre\": \"2026-08-23T15:05:04\",\n  \"duracion_proceso_segundos\": 304,\n  \"agente\": \"personal-training-booking-agent\"\n}\n```\n\n---\n\n## 5. Registro de escalamiento a humano (JSON)\n\nSe genera cuando se activa una condición de escalamiento (ver `reglas-escalamiento.md`).\n\n```json\n{\n  \"registro_id\": \"PTBREG-YYYYMMDD-HHMMSS-XXX\",\n  \"solicitud_id\": \"PTB-YYYYMMDD-HHMMSS-XXX\",\n  \"evento\": \"escalamiento\",\n  \"fecha_hora_ejecucion\": \"YYYY-MM-DDTHH:MM:SS\",\n  \"agente\": \"personal-training-booking-agent\",\n  \"gimnasio_id\": \"string\",\n  \"gimnasio_nombre\": \"string\",\n  \"nombre_contacto\": \"string\",\n  \"telefono_contacto\": \"string\",\n  \"email_contacto\": \"string | null\",\n  \"canal_origen\": \"whatsapp | sms | email | app | web\",\n  \"tipo_sesion_solicitada\": \"string\",\n  \"entrenador_preferido\": \"string | null\",\n  \"fecha_preferida\": \"YYYY-MM-DD | null\",\n  \"hora_preferida\": \"HH:MM | null\",\n  \"resultado\": \"escalamiento\",\n  \"detalles\": \"Motivo: {{motivo_escalamiento}}.\",\n  \"responsable_escalamiento\": \"string nombre del responsable\",\n  \"canal_escalamiento\": \"whatsapp_interno | email_interno\",\n  \"plazo_respuesta_horas\": 0,\n  \"datos_origen\": {\n    \"solicitud_id\": \"{{solicitud_id}}\",\n    \"nombre_contacto\": \"{{nombre_contacto}}\",\n    \"telefono_contacto\": \"{{telefono_contacto}}\",\n    \"tipo_sesion_solicitada\": \"{{tipo_sesion_solicitada}}\",\n    \"fecha_preferida\": \"{{fecha_preferida}}\",\n    \"hora_preferida\": \"{{hora_preferida}}\"\n  }\n}\n```\n\n### Ejemplo — Sin entrenadores disponibles\n\n```json\n{\n  \"registro_id\": \"PTBREG-20260823-110010-003\",\n  \"solicitud_id\": \"PTB-20260823-110000-003\",\n  \"evento\": \"escalamiento\",\n  \"fecha_hora_ejecucion\": \"2026-08-23T11:00:10\",\n  \"agente\": \"personal-training-booking-agent\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"nombre_contacto\": \"Laura Hernández\",\n  \"telefono_contacto\": \"+52 55 1122 3344\",\n  \"email_contacto\": null,\n  \"canal_origen\": \"whatsapp\",\n  \"tipo_sesion_solicitada\": \"REHABILITACION\",\n  \"entrenador_preferido\": null,\n  \"fecha_preferida\": \"2026-08-25\",\n  \"hora_preferida\": \"07:00\",\n  \"resultado\": \"escalamiento\",\n  \"detalles\": \"Motivo: Sin entrenadores disponibles con especialidad en REHABILITACION en los próximos 7 días. No se pueden ofrecer opciones al contacto.\",\n  \"responsable_escalamiento\": \"Carlos Mendoza\",\n  \"canal_escalamiento\": \"whatsapp_interno\",\n  \"plazo_respuesta_horas\": 4,\n  \"datos_origen\": {\n    \"solicitud_id\": \"PTB-20260823-110000-003\",\n    \"nombre_contacto\": \"Laura Hernández\",\n    \"telefono_contacto\": \"+52 55 1122 3344\",\n    \"tipo_sesion_solicitada\": \"REHABILITACION\",\n    \"fecha_preferida\": \"2026-08-25\",\n    \"hora_preferida\": \"07:00\"\n  }\n}\n```\n\n---\n\n## 6. Formato de fecha para mensajes al contacto\n\n| Elemento | Formato | Ejemplo |\n|---|---|---|\n| `fecha_formateada` | Día + mes + año en español | `\"25 de agosto de 2026\"` |\n| `dia_semana` | Día de la semana en español | `\"Lunes\"` |\n\n### Tabla de meses\n\n| Mes | Formato |\n|---|---|\n| Enero | `\"enero\"` |\n| Febrero | `\"febrero\"` |\n| Marzo | `\"marzo\"` |\n| Abril | `\"abril\"` |\n| Mayo | `\"mayo\"` |\n| Junio | `\"junio\"` |\n| Julio | `\"julio\"` |\n| Agosto | `\"agosto\"` |\n| Septiembre | `\"septiembre\"` |\n| Octubre | `\"octubre\"` |\n| Noviembre | `\"noviembre\"` |\n| Diciembre | `\"diciembre\"` |\n\n### Tabla de tipos de sesión (nombre legible)\n\n| Tipo de sesión | Nombre legible |\n|---|---|\n| `FUERZA` | `\"Fuerza\"` |\n| `FUNCIONAL` | `\"Funcional\"` |\n| `CARDIO` | `\"Cardio\"` |\n| `FLEXIBILIDAD` | `\"Flexibilidad\"` |\n| `REHABILITACION` | `\"Rehabilitación\"` |\n| `EVALUACION` | `\"Evaluación\"` |\n"
              },
              {
                "id": "personal-training-booking-agent/prompt-sistema.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "prompt-sistema.md",
                "contenido": "# Prompt del sistema — Personal Training Booking Agent\n\n## Identidad\nEres **FitBot**, asistente virtual de **GymPro Fitness Center**. Tu función es gestionar el agendamiento de sesiones de entrenamiento personal de forma profesional y orientada a resultados, ofreciendo horarios disponibles con entrenadores certificados.\n\n## Tono de comunicación\n- **Profesional y cercano**: el cliente está invirtiendo en su salud y rendimiento. Tratarlo con la seriedad que merece una inversión personal.\n- **Orientado a resultados**: cada mensaje refuerza el compromiso del cliente con sus metas. No es solo una reserva, es un paso hacia su objetivo.\n- **Organizado y concreto**: cada respuesta incluye fechas, horarios, nombre del entrenador y tipo de sesión. Sin ambigüedades.\n- **Breve pero completo**: máximo 4 párrafos por mensaje. Incluir todos los datos necesarios de la reserva.\n- **Sin presión**: respetar el ritmo del cliente. No usar frases de cierre agresivas.\n\n## Flujo de ejecución\nEjecuta los siguientes pasos en orden exacto, sin omitir ni reordenar:\n\n### Paso 1 — Recepción de la solicitud de cita/reserva con fecha y servicio deseado\nRecibe la solicitud de una sesión de entrenamiento personal de un contacto (prospecto nuevo o miembro actual) a través de cualquier canal configurado.\n\n### Paso 2 — Consulta de disponibilidad en el calendario conectado\nConsulta el sistema de calendario del gimnasio para verificar la disponibilidad de horarios según el tipo de sesión, la fecha/hora preferida y el entrenador preferido.\n\n### Paso 3 — Ofrecimiento de hasta 3 horarios disponibles concretos\nGenera y envía al usuario un mensaje con las opciones de horario disponibles para su sesión de entrenamiento personal.\n\n### Paso 4 — Confirmación de la selección del usuario y creación del evento en el calendario\nRecibe la selección del usuario, verifica la disponibilidad del horario seleccionado y crea el evento en el calendario del gimnasio.\n\n### Paso 5 — Envío de confirmación por escrito al usuario con fecha, hora y detalles\nEnvía al usuario un mensaje de confirmación completo con todos los datos de la reserva.\n\n### Paso 6 — Registro del nuevo contacto o actualización del existente en la base de datos\nRegistra o actualiza la información del contacto en la base de datos del gimnasio.\n\n## Ejemplo de ejecución\n\n**Entrada:**\n- nombre_cliente: \"Ana García López\"\n- telefono: \"+52-55-9876-5432\"\n- consulta: \"Quiero agendar una sesión de entrenamiento personal de fuerza para el lunes por la mañana, con el coach Martínez si es posible\"\n\n**Salida generada (paso 3 — ofrecimiento):**\n```\n¡Hola Ana García! 💪 Consulté la disponibilidad de entrenamiento personal y estas son las opciones:\n\n📅 Opción 1: Lunes 25 de agosto de 2026 a las 07:00 — Fuerza con Coach Martínez\n📅 Opción 2: Miércoles 27 de agosto de 2026 a las 18:00 — Funcional con Coach Herrera\n📅 Opción 3: Viernes 29 de agosto de 2026 a las 09:00 — Fuerza con Coach Martínez\n\n¿Cuál te conviene más? Responde con el número (1, 2 o 3).\n```\n\n## Tipos de sesión de entrenamiento personal\n\n| ID | Tipo de sesión | Duración (min) | Precio sugerido | Descripción |\n|---|---|---|---|---|\n| `FUERZA` | Fuerza y resistencia | 60 | $350 MXN | Rutina personalizada de pesas, máquinas y ejercicios de fuerza |\n| `FUNCIONAL` | Entrenamiento funcional | 60 | $350 MXN | Ejercicios funcionales, movimientos compuestos, trabajo cardiovascular |\n| `CARDIO` | Cardio intensivo | 45 | $300 MXN | Cardio personalizado: bicicleta, remo, elíptica, HIIT |\n| `FLEXIBILIDAD` | Flexibilidad y movilidad | 45 | $300 MXN | Estiramientos, yoga, movilidad articular, relajación |\n| `REHABILITACION` | Rehabilitación deportiva | 60 | $400 MXN | Ejercicios de rehabilitación post-lesión, fortalecimiento específico |\n| `EVALUACION` | Evaluación inicial | 45 | Gratis | Evaluación física completa, composición corporal, prueba de fuerza |\n\n## Restricciones\n- No reservar en horarios fuera del horario de atención del gimnasio.\n- No compartir información de otros clientes. Cada reserva es independiente y confidencial.\n- No confirmar sin selección explícita del usuario. Siempre esperar que responda con el número de opción.\n- Incluir siempre el nombre del entrenador en la oferta y confirmación de reserva.\n- Incluir siempre la dirección o sala del gimnasio en la confirmación.\n- Incluir siempre el código de sesión en la confirmación.\n\n## Reglas de escalamiento\n- Si no hay horarios disponibles en los próximos 7 días → escalar a humano para coordinación manual.\n- Si el entrenador preferido no tiene disponibilidad → escalar a humano.\n- Si el envío de confirmación falla 2 veces consecutivas → escalar a humano.\n- Si hay conflicto de reservas o superposición de eventos → escalar a humano.\n- Tiempo máximo de respuesta: 2 minutos. Si excede, escalar a equipo humano.\n"
              },
              {
                "id": "personal-training-booking-agent/reglas-escalamiento.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "reglas-escalamiento.md",
                "contenido": "# Reglas de escalamiento — Personal Training Booking Agent\n\nEste archivo define la lista cerrada y exacta de condiciones bajo las cuales el agente **debe** transferir el caso a un humano (coordinador de entrenadores, gerente o responsable de atención al cliente). No existe discrecionalidad: si se cumple alguna condición, el escalamiento es **obligatorio**.\n\n---\n\n## Condiciones de escalamiento\n\n### C1 — Datos del contacto inválidos\n\n| # | Condición | Acción |\n|---|---|---|\n| C1.1 | El `contacto_id` tiene formato inválido o está vacío | Escalamiento obligatorio. Motivo: `ID de contacto inválido o vacío asociado a la solicitud de reserva.` No se procesa la reserva. |\n| C1.2 | El `nombre_contacto` está vacío, es `null` o es genérico (ej. \"Contacto\", \"Sin nombre\") | Escalamiento obligatorio. Motivo: `Nombre del contacto no disponible o genérico. No se puede personalizar la reserva.` No se procesa la reserva hasta confirmar identidad. |\n| C1.3 | El `telefono_contacto` tiene menos de 8 dígitos o más de 15 dígitos y `canal_origen` es `whatsapp` o `sms` | Escalamiento obligatorio. Motivo: `Teléfono del contacto '{{telefono_contacto}}' no cumple con el formato válido (8-15 dígitos). No es posible enviar confirmación por {{canal_origen}}.` No se procesa la reserva. |\n| C1.4 | El `canal_origen` es `email` pero el `email_remitente` es `null` o está vacío | Escalamiento obligatorio. Motivo: `Canal configurado como 'email' pero el contacto no tiene email registrado. No es posible enviar confirmación.` No se procesa la reserva. |\n| C1.5 | El `email_remitente` está presente pero no tiene formato válido de email | Escalamiento obligatorio. Motivo: `Email del contacto '{{email_remitente}}' no tiene formato válido. No es posible enviar confirmación por email.` No se procesa la reserva. |\n\n---\n\n### C2 — Disponibilidad de entrenadores y calendario\n\n| # | Condición | Acción |\n|---|---|---|\n| C2.1 | `entrenadores_disponibles` está vacío o no contiene ningún entrenador con `activo: true` | Escalamiento obligatorio. Motivo: `No hay entrenadores personales activos disponibles en el sistema. No se pueden procesar reservas de entrenamiento personal.` No se procesa la reserva. |\n| C2.2 | No hay **ningún entrenador** con especialidad en el `tipo_sesion_solicitada` | Escalamiento obligatorio. Motivo: `Sin entrenadores disponibles con especialidad en {{tipo_sesion_solicitada}} en los próximos 7 días. No se pueden ofrecer opciones al contacto.` No se envía oferta de horarios. |\n| C2.3 | No se encontró **ningún horario disponible** en los próximos 7 días para el tipo de sesión solicitado | Escalamiento obligatorio. Motivo: `Sin horarios disponibles en los próximos 7 días para {{tipo_sesion_solicitada}}. No se pueden ofrecer opciones al contacto.` No se envía oferta de horarios. Se notifica al equipo interno para coordinar disponibilidad manual. Plazo: **2 horas**. |\n| C2.4 | `calendario_conectado` es `false` o el calendario no está operativo | Escalamiento obligatorio. Motivo: `Calendario no conectado o no operativo. No se puede consultar disponibilidad para reservar.` No se procesa la reserva. |\n| C2.5 | La `fecha_preferida` indicada por el usuario es anterior a la fecha actual o tiene más de 14 días de anticipación | Escalamiento obligatorio. Motivo: `Fecha preferida '{{fecha_preferida}}' fuera del rango permitido (debe ser entre hoy y {{dias_calendario_consulta}} días).` No se procesa la reserva. Se informa al contacto el rango válido. |\n| C2.6 | La `hora_preferida` indicada por el usuario está fuera del horario de atención del gimnasio | Escalamiento obligatorio. Motivo: `Hora preferida '{{hora_preferida}}' fuera del horario de atención ({{horario_atencion_inicio}}–{{horario_atencion_fin}}).` No se procesa la reserva. Se ofrecen horarios dentro del horario válido. |\n\n---\n\n### C3 — Fallo técnico de envío\n\n| # | Condición | Acción |\n|---|---|---|\n| C3.1 | El envío del mensaje de ofrecimiento de horarios falla **2 veces consecutivas** (intento original + 1 reintento tras 30 segundos) | Escalamiento obligatorio. Motivo: `Error de envío por canal {{canal_origen}} tras 2 intentos fallidos al enviar oferta de horarios a {{nombre_contacto}} (solicitud {{solicitud_id}}).` Generar registro de escalamiento. Notificar al equipo interno para contacto directo. Plazo: **2 horas**. |\n| C3.2 | El envío del mensaje de confirmación de reserva falla **2 veces consecutivas** (intento original + 1 reintento tras 30 segundos) | Escalamiento obligatorio. Motivo: `Error de envío de confirmación por canal {{canal_origen}} tras 2 intentos fallidos. Reserva generada en calendario pero confirmación no entregada a {{nombre_contacto}} (solicitud {{solicitud_id}}).` Generar registro de escalamiento. Notificar al equipo interno para confirmación manual. Plazo: **2 horas**. |\n| C3.3 | El `canal_origen` no es uno de los 5 valores válidos (`whatsapp`, `sms`, `email`, `app`, `web`) | Escalamiento obligatorio. Motivo: `Canal de origen '{{canal_origen}}' no reconocido para la solicitud {{solicitud_id}}.` No se procesa la reserva. |\n| C3.4 | El destino del envío (número de teléfono, email o token) está vacío o es inválido | Escalamiento obligatorio. Motivo: `Destino de envío inválido o vacío para canal {{canal_origen}}.` No se envía oferta ni confirmación. |\n\n---\n\n### C4 — Creación del evento en el calendario fallida\n\n| # | Condición | Acción |\n|---|---|---|\n| C4.1 | La creación del evento en el calendario conectado falla después de **2 reintentos** (10 segundos entre cada uno) | Escalamiento obligatorio. Motivo: `Error al crear evento en calendario ({{calendario_tipo}}) tras 2 intentos fallidos. Reserva de {{tipo_sesion_solicitada}} para {{nombre_contacto}} el {{fecha_reserva}} a las {{hora_inicio}} no se pudo registrar.` Generar registro de escalamiento. Notificar al equipo interno para registro manual. Plazo: **2 horas**. |\n| C4.2 | El evento se creó en el calendario pero el `evento_calendario_id` no se pudo obtener | Escalamiento obligatorio. Motivo: `Evento creado en calendario pero sin ID de seguimiento. No se puede verificar la reserva de {{nombre_contacto}} para {{fecha_reserva}} a las {{hora_inicio}}.` Generar registro de escalamiento. Notificar al equipo interno para verificación. Plazo: **4 horas**. |\n| C4.3 | El horario seleccionado por el usuario ya no está disponible en el calendario al momento de intentar crear el evento (sesión del entrenador ya agendada) | Escalamiento obligatorio. Motivo: `Horario seleccionado ({{fecha_reserva}} {{hora_inicio}}) ya no disponible al momento de confirmar. Sesión del entrenador {{entrenador_nombre}} ya agendada externamente. No se puede completar la reserva de {{nombre_contacto}}.` Generar registro de escalamiento. Notificar al equipo interno para coordinar alternativa. Plazo: **2 horas**. |\n\n---\n\n### C5 — Restricciones del contacto o membresía\n\n| # | Condición | Acción |\n|---|---|---|\n| C5.1 | El contacto es miembro actual pero `sesiones_personal_consumidas` ≥ `sesiones_personal_incluidas` y no indica que desea pagar sesión adicional | Notificar al contacto que ha agotado sus sesiones incluidas. Ofrecer opción de compra de sesión adicional. Si el contacto confirma compra, proceder con reserva a precio regular. Si no confirma, escalar a humano para asesoría. Plazo: **4 horas**. |\n| C5.2 | El contacto indica una lesión o limitación grave en `lesiones_limitaciones` que requiere evaluación médica previa | Escalamiento obligatorio. Motivo: `Contacto indica lesión o limitación: '{{lesiones_limitaciones}}'. Requiere evaluación médica previa antes de agendar sesión de entrenamiento personal.` No se procesa la reserva hasta obtener conformidad médica. Notificar al equipo interno. Plazo: **4 horas**. |\n| C5.3 | El `tipo_sesion_solicitada` es `REHABILITACION` pero el contacto no tiene evaluación médica registrada | Escalamiento obligatorio. Motivo: `Sesión de rehabilitación solicitada sin evaluación médica previa registrada. Requiere verificación de condición médica del contacto {{nombre_contacto}}.` No se procesa la reserva. Notificar al equipo interno. Plazo: **4 horas**. |\n\n---\n\n### C6 — Restricciones del entrenador\n\n| # | Condición | Acción |\n|---|---|---|\n| C6.1 | El entrenador solicitado (`entrenador_preferido`) no tiene disponibilidad en los próximos 7 días | No escalar inmediatamente. Ofrecer alternativas con otros entrenadores. Si el contacto insiste en el entrenador específico, escalar a humano. Motivo: `Entrenador '{{entrenador_preferido}}' sin disponibilidad en los próximos 7 días. Contacto insiste en este entrenador específico.` Generar registro de escalamiento. Plazo: **4 horas**. |\n| C6.2 | El entrenador solicitado (`entrenador_preferido`) no tiene especialidad en el `tipo_sesion_solicitada` | Escalamiento obligatorio. Motivo: `Entrenador '{{entrenador_preferido}}' no tiene especialidad en {{tipo_sesion_solicitada}}. No se puede asignar para esta sesión.` No se procesa la reserva con este entrenador. Ofrecer alternativas. |\n\n---\n\n## Resumen de tiempos de escalamiento\n\n| Condición | Tiempo antes de escalar | Prioridad |\n|---|---|---|\n| Contacto_id inválido (C1.1) | 0 segundos (inmediato) | Normal |\n| Nombre genérico (C1.2) | 0 segundos (inmediato) | Normal |\n| Teléfono inválido (C1.3) | 0 segundos (inmediato) | Normal |\n| Email faltante (C1.4) | 0 segundos (inmediato) | Normal |\n| Email inválido (C1.5) | 0 segundos (inmediato) | Normal |\n| Sin entrenadores activos (C2.1) | 0 segundos (inmediato) | Normal |\n| Sin entrenador para tipo sesión (C2.2) | 0 segundos (inmediato) | Normal |\n| Sin horarios disponibles (C2.3) | 0 segundos (inmediato) | Normal |\n| Calendario no conectado (C2.4) | 0 segundos (inmediato) | Normal |\n| Fecha fuera de rango (C2.5) | 0 segundos (inmediato) | Normal |\n| Hora fuera de horario (C2.6) | 0 segundos (inmediato) | Normal |\n| Fallo envío oferta 2 intentos (C3.1) | 0 segundos tras 1 reintento fallido | Normal |\n| Fallo envío confirmación 2 intentos (C3.2) | 0 segundos tras 1 reintento fallido | **Alta** |\n| Canal no reconocido (C3.3) | 0 segundos (inmediato) | Normal |\n| Destino inválido (C3.4) | 0 segundos (inmediato) | Normal |\n| Fallo creación evento 2 intentos (C4.1) | 0 segundos tras 1 reintento fallido | **Alta** |\n| Evento sin ID (C4.2) | 0 segundos (inmediato) | Normal |\n| Horario ya no disponible (C4.3) | 0 segundos (inmediato) | **Alta** |\n| Sesiones agotadas (C5.1) | 0 segundos (inmediato) | Normal |\n| Lesión grave (C5.2) | 0 segundos (inmediato) | Normal |\n| Rehabilitación sin evaluación (C5.3) | 0 segundos (inmediato) | Normal |\n| Entrenador sin disponibilidad (C6.1) | Solo si insiste el contacto | Normal |\n| Entrenador sin especialidad (C6.2) | 0 segundos (inmediato) | Normal |\n\n---\n\n## Plazos de respuesta del equipo interno\n\n| Tipo de escalamiento | Plazo de respuesta esperado |\n|---|---|\n| Sin horarios disponibles (C2.3) | 2 horas |\n| Horario ya no disponible (C4.3) | 2 horas |\n| Fallo envío oferta (C3.1) | 2 horas |\n| Fallo envío confirmación (C3.2) | 2 horas |\n| Fallo creación evento (C4.1) | 2 horas |\n| Datos del contacto inválidos (C1.1–C1.5) | 4 horas |\n| Entrenador sin especialidad (C6.2) | 4 horas |\n| Sesiones agotadas (C5.1) | 4 horas |\n| Lesión grave (C5.2) | 4 horas |\n| Rehabilitación sin evaluación (C5.3) | 4 horas |\n| Entrenador sin disponibilidad (C6.1) | 4 horas |\n| Evento sin ID (C4.2) | 4 horas |\n| Calendario no conectado (C2.4) | 4 horas |\n\n---\n\n## Proceso de escalamiento\n\nCuando se activa cualquiera de las condiciones C1–C6:\n\n1. El agente genera el registro de escalamiento con `resultado: \"escalamiento\"` usando el formato de `formato-salida.md` → sección 5.\n2. El agente envía al contacto el mensaje de escalamiento (excepto para C1.1, C1.3, C1.4, C2.1, C2.4, C3.1, C3.4 donde no se puede enviar respuesta al contacto).\n3. El agente notifica al equipo interno con los datos del registro de escalamiento.\n4. El agente actualiza `resultado` a `escalamiento` y `fue_escalado` a `true`.\n5. El agente espera la resolución del equipo interno dentro del plazo correspondiente.\n6. Si el equipo interno no responde dentro del plazo, el agente notifica al responsable con nivel de urgencia correspondiente.\n\n---\n\n## Textos base de escalamiento\n\nLos siguientes textos se usan literalmente en los registros de escalamiento. No se deben modificar ni parafrasear.\n\n| Condición | Texto del campo `detalles` |\n|---|---|\n| C1.1 | `\"Motivo: ID de contacto inválido o vacío asociado a la solicitud de reserva.\"` |\n| C1.2 | `\"Motivo: Nombre del contacto no disponible o genérico. No se puede personalizar la reserva.\"` |\n| C1.3 | `\"Motivo: Teléfono del contacto '{{telefono_contacto}}' no cumple con el formato válido (8-15 dígitos). No es posible enviar confirmación por {{canal_origen}}.\"` |\n| C1.4 | `\"Motivo: Canal configurado como 'email' pero el contacto no tiene email registrado. No es posible enviar confirmación.\"` |\n| C1.5 | `\"Motivo: Email del contacto '{{email_remitente}}' no tiene formato válido. No es posible enviar confirmación por email.\"` |\n| C2.1 | `\"Motivo: No hay entrenadores personales activos disponibles en el sistema. No se pueden procesar reservas de entrenamiento personal.\"` |\n| C2.2 | `\"Motivo: Sin entrenadores disponibles con especialidad en {{tipo_sesion_solicitada}} en los próximos 7 días. No se pueden ofrecer opciones al contacto.\"` |\n| C2.3 | `\"Motivo: Sin horarios disponibles en los próximos 7 días para {{tipo_sesion_solicitada}}. No se pueden ofrecer opciones al contacto.\"` |\n| C2.4 | `\"Motivo: Calendario no conectado o no operativo. No se puede consultar disponibilidad para reservar.\"` |\n| C2.5 | `\"Motivo: Fecha preferida '{{fecha_preferida}}' fuera del rango permitido (debe ser entre hoy y {{dias_calendario_consulta}} días).\"` |\n| C2.6 | `\"Motivo: Hora preferida '{{hora_preferida}}' fuera del horario de atención ({{horario_atencion_inicio}}–{{horario_atencion_fin}}).\"` |\n| C3.1 | `\"Motivo: Error de envío por canal {{canal_origen}} tras 2 intentos fallidos al enviar oferta de horarios a {{nombre_contacto}} (solicitud {{solicitud_id}}).\"` |\n| C3.2 | `\"Motivo: Error de envío de confirmación por canal {{canal_origen}} tras 2 intentos fallidos. Reserva generada en calendario pero confirmación no entregada a {{nombre_contacto}} (solicitud {{solicitud_id}}).\"` |\n| C3.3 | `\"Motivo: Canal de origen '{{canal_origen}}' no reconocido para la solicitud {{solicitud_id}}.\"` |\n| C3.4 | `\"Motivo: Destino de envío inválido o vacío para canal {{canal_origen}}.\"` |\n| C4.1 | `\"Motivo: Error al crear evento en calendario ({{calendario_tipo}}) tras 2 intentos fallidos. Reserva de {{tipo_sesion_solicitada}} para {{nombre_contacto}} el {{fecha_reserva}} a las {{hora_inicio}} no se pudo registrar.\"` |\n| C4.2 | `\"Motivo: Evento creado en calendario pero sin ID de seguimiento. No se puede verificar la reserva de {{nombre_contacto}} para {{fecha_reserva}} a las {{hora_inicio}}.\"` |\n| C4.3 | `\"Motivo: Horario seleccionado ({{fecha_reserva}} {{hora_inicio}}) ya no disponible al momento de confirmar. Sesión del entrenador {{entrenador_nombre}} ya agendada externamente. No se puede completar la reserva de {{nombre_contacto}}.\"` |\n| C5.1 | `\"Motivo: Miembro ha agotado sus {{sesiones_personal_incluidas}} sesiones incluidas en el plan {{plan_actual}}. Contacto {{nombre_contacto}}. Requiere asesoría para compra de sesión adicional o upgrade de plan.\"` |\n| C5.2 | `\"Motivo: Contacto indica lesión o limitación: '{{lesiones_limitaciones}}'. Requiere evaluación médica previa antes de agendar sesión de entrenamiento personal.\"` |\n| C5.3 | `\"Motivo: Sesión de rehabilitación solicitada sin evaluación médica previa registrada. Requiere verificación de condición médica del contacto {{nombre_contacto}}.\"` |\n| C6.1 | `\"Motivo: Entrenador '{{entrenador_preferido}}' sin disponibilidad en los próximos 7 días. Contacto insiste en este entrenador específico.\"` |\n| C6.2 | `\"Motivo: Entrenador '{{entrenador_preferido}}' no tiene especialidad en {{tipo_sesion_solicitada}}. No se puede asignar para esta sesión.\"` |\n\n---\n\n## Ejemplo completo de activación\n\n**Condición activada:** C5.1 (miembro agotó sesiones incluidas)\n\n**Contexto:** El miembro Roberto Ruiz Méndez (plan Premium, 2 sesiones incluidas, 2 consumidas) solicita una sesión de fuerza.\n\n**Flujo del agente:**\n1. Recibe solicitud: \"Quiero agendar una sesión de fuerza para el martes\"\n2. Detecta que el miembro agotó sus 2 sesiones incluidas.\n3. Notifica al contacto que agotó sus sesiones y ofrece opción de compra.\n4. El contacto confirma que desea comprar sesión adicional.\n5. Se procede con la reserva a precio regular ($350 MXN).\n\n**Respuesta al contacto:**\n\n> ¡Hola Roberto Ruiz! 💪 Consulté tu plan actual (Premium) y las 2 sesiones de entrenamiento personal incluidas ya fueron utilizadas.\n>\n> 💰 **Opción:** Sesión adicional por $350 MXN\n> 📅 **Horarios disponibles:**\n> • Martes 26 de agosto de 2026 a las 07:00 — Fuerza con Coach Martínez\n> • Miércoles 27 de agosto de 2026 a las 16:00 — Funcional con Coach Herrera\n>\n> ¿Deseas reservar una sesión adicional? Responde \"🔄 Comprar sesión\" o \"📋 Ver planes\".\n> — FitZone Premium\n\n**Registro de escalamiento (si el contacto no confirma compra):**\n\n```json\n{\n  \"registro_id\": \"PTBREG-20260823-140010-004\",\n  \"solicitud_id\": \"PTB-20260823-140000-004\",\n  \"evento\": \"escalamiento\",\n  \"fecha_hora_ejecucion\": \"2026-08-23T14:00:10\",\n  \"agente\": \"personal-training-booking-agent\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"nombre_contacto\": \"Roberto Ruiz Méndez\",\n  \"telefono_contacto\": \"+52 55 4567 8901\",\n  \"email_contacto\": \"roberto.ruiz@correo.com\",\n  \"canal_origen\": \"whatsapp\",\n  \"tipo_sesion_solicitada\": \"FUERZA\",\n  \"entrenador_preferido\": null,\n  \"fecha_preferida\": \"2026-08-26\",\n  \"hora_preferida\": null,\n  \"resultado\": \"escalamiento\",\n  \"detalles\": \"Motivo: Miembro ha agotado sus 2 sesiones incluidas en el plan Premium. Contacto Roberto Ruiz Méndez. Requiere asesoría para compra de sesión adicional o upgrade de plan.\",\n  \"responsable_escalamiento\": \"Carlos Mendoza\",\n  \"canal_escalamiento\": \"whatsapp_interno\",\n  \"plazo_respuesta_horas\": 4,\n  \"datos_origen\": {\n    \"solicitud_id\": \"PTB-20260823-140000-004\",\n    \"nombre_contacto\": \"Roberto Ruiz Méndez\",\n    \"telefono_contacto\": \"+52 55 4567 8901\",\n    \"tipo_sesion_solicitada\": \"FUERZA\",\n    \"fecha_preferida\": \"2026-08-26\",\n    \"hora_preferida\": null\n  }\n}\n```\n"
              },
              {
                "id": "personal-training-booking-agent/variables-entrada.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "variables-entrada.md",
                "contenido": "# Variables de entrada — Personal Training Booking Agent\n\nEste archivo define la lista fija y cerrada de campos de datos que el agente necesita recibir para operar. No se admiten campos adicionales a los listados aquí.\n\n---\n\n## Campos del evento disparador\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 1 | `evento_id` | `string` | Obligatorio | Identificador único del evento. Formato: `PTB-YYYYMMDD-HHMMSS-XXX`. | `\"PTB-20260823-103000-001\"` |\n| 2 | `contacto_id` | `string` | Obligatorio | Identificador del contacto asociado. Formato: `CON-YYYYMMDD-XXX`. | `\"CON-20260823-201\"` |\n| 3 | `mensaje_solicitud` | `string` | Obligatorio | Mensaje o texto libre de la solicitud del contacto. Mínimo 3 caracteres, máximo 2000. | `\"Hola, quiero agendar una sesión de entrenamiento personal de fuerza para el lunes por la mañana\"` |\n| 4 | `canal_origen` | `string` (enum) | Obligatorio | Canal por el que se recibió la solicitud. Valores: `whatsapp`, `sms`, `email`, `app`, `web`. | `\"whatsapp\"` |\n| 5 | `numero_remitente` | `string` | Obligatorio (requerido para whatsapp/sms) | Número telefónico del remitente. Entre 8 y 15 dígitos. | `\"+52 55 9876 5432\"` |\n| 6 | `email_remitente` | `string` | Opcional (requerido si `canal_origen` = `email`) | Correo electrónico del remitente. | `\"ana.garcia@correo.com\"` |\n| 7 | `fecha_evento` | `string` (ISO 8601: `YYYY-MM-DD`) | Obligatorio | Fecha en que se recibió la solicitud. | `\"2026-08-23\"` |\n| 8 | `hora_evento` | `string` (formato 24h: `HH:MM`) | Opcional | Hora en que se recibió la solicitud. | `\"10:30\"` |\n\n---\n\n## Datos del contacto\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 9 | `nombre_contacto` | `string` | Obligatorio | Nombre completo del contacto. | `\"Ana García López\"` |\n| 10 | `telefono_contacto` | `string` | Obligatorio | Número telefónico del contacto. Entre 8 y 15 dígitos. | `\"+52 55 9876 5432\"` |\n| 11 | `email_contacto` | `string` | Opcional | Correo electrónico del contacto. | `\"ana.garcia@correo.com\"` |\n| 12 | `es_miembro_actual` | `boolean` | Obligatorio | `true` si el contacto ya es miembro activo del gimnasio. `false` si es prospecto nuevo. | `true` |\n| 13 | `membresia_id` | `string` | Opcional (requerido si `es_miembro_actual` = `true`) | Identificador de la membresía activa. Formato: `MEM-YYYYMMDD-XXX`. | `\"MEM-20260115-101\"` |\n| 14 | `plan_actual` | `string` | Opcional (requerido si `es_miembro_actual` = `true`) | Plan de membresía actual. Valores: `basico`, `premium`, `elite`. | `\"elite\"` |\n| 15 | `sesiones_personal_incluidas` | `integer` | Opcional (requerido si `es_miembro_actual` = `true`) | Número de sesiones de entrenamiento personal incluidas en el plan. | `4` |\n| 16 | `sesiones_personal_consumidas` | `integer` | Opcional (requerido si `es_miembro_actual` = `true`) | Número de sesiones de entrenamiento personal ya consumidas. | `2` |\n\n---\n\n## Datos de la solicitud de reserva\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 17 | `tipo_sesion_solicitada` | `string` (enum) | Obligatorio | Tipo de sesión que el contacto desea agendar. Valores: `FUERZA`, `FUNCIONAL`, `CARDIO`, `FLEXIBILIDAD`, `REHABILITACION`, `EVALUACION`. | `\"FUERZA\"` |\n| 18 | `entrenador_preferido` | `string` | Opcional | Nombre del entrenador preferido por el contacto. | `\"Coach Martínez\"` |\n| 19 | `fecha_preferida` | `string` (ISO 8601: `YYYY-MM-DD`) | Opcional | Fecha preferida por el contacto. Si no se proporciona, el agente ofrece las opciones más cercanas. | `\"2026-08-25\"` |\n| 20 | `hora_preferida` | `string` (formato 24h: `HH:MM`) | Opcional | Hora preferida por el contacto. Si no se proporciona, el agente ofrece horarios dentro del horario de atención. | `\"07:00\"` |\n| 21 | `objetivo_entrenamiento` | `string` | Opcional | Objetivo fitness del contacto (ej. \"ganar masa muscular\", \"bajar de peso\", \"mejorar resistencia\", \"rehabilitación\"). | `\"ganar masa muscular\"` |\n| 22 | `notas_especiales` | `string` | Opcional | Notas o restricciones especiales (ej. \"tengo una lesión en la rodilla\", \"soy principiante\"). | `\"Soy principiante, nunca he entrenado con pesas\"` |\n| 23 | `lesiones_limitaciones` | `string` | Opcional | Lesiones o limitaciones físicas conocidas. | `\"Lesión antigua en hombro derecho\"` |\n\n---\n\n## Datos de entrenadores (inyectados desde configuración)\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 24 | `entrenadores_disponibles` | `array` de objects | Obligatorio | Lista de entrenadores personales del gimnasio. (ver estructura) | (ver abajo) |\n\n### Estructura de cada entrenador en `entrenadores_disponibles`\n\n```json\n{\n  \"entrenador_id\": \"TRN-001\",\n  \"entrenador_nombre\": \"Coach Martínez\",\n  \"especialidades\": [\"FUERZA\", \"FUNCIONAL\"],\n  \"horarios\": [\n    {\"dia\": \"lunes\", \"hora_inicio\": \"06:00\", \"hora_fin\": \"14:00\"},\n    {\"dia\": \"miércoles\", \"hora_inicio\": \"06:00\", \"hora_fin\": \"14:00\"},\n    {\"dia\": \"viernes\", \"hora_inicio\": \"06:00\", \"hora_fin\": \"14:00\"}\n  ],\n  \"sesiones_maximas_dia\": 8,\n  \"disponible_prueba\": true,\n  \"activo\": true\n}\n```\n\n| Campo | Tipo | Obligatorio | Descripción |\n|---|---|---|---|\n| `entrenador_id` | `string` | Obligatorio | ID único del entrenador. Formato: `TRN-XXX`. |\n| `entrenador_nombre` | `string` | Obligatorio | Nombre legible del entrenador. |\n| `especialidades` | `array` de strings | Obligatorio | Tipos de sesión que puede impartir. |\n| `horarios` | `array` de objects | Obligatorio | Horarios disponibles por día de la semana. |\n| `sesiones_maximas_dia` | `integer` | Obligatorio | Máximo de sesiones que puede dar por día. |\n| `disponible_prueba` | `boolean` | Obligatorio | `true` si puede impartir sesión de evaluación inicial. |\n| `activo` | `boolean` | Obligatorio | `true` si el entrenador está disponible para reservas. |\n\n---\n\n## Datos del gimnasio (inyectados desde configuración)\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 25 | `gimnasio_id` | `string` | Obligatorio | Identificador único del gimnasio. | `\"GYM-001\"` |\n| 26 | `gimnasio_nombre` | `string` | Obligatorio | Nombre comercial del gimnasio. | `\"FitZone Premium\"` |\n| 27 | `gimnasio_telefono` | `string` | Obligatorio | Teléfono principal del gimnasio. | `\"+52 55 4321 8765\"` |\n| 28 | `gimnasio_email` | `string` | Obligatorio | Email general del gimnasio. | `\"info@fitzonepremium.com\"` |\n| 29 | `gimnasio_direccion` | `string` | Obligatorio | Dirección completa del gimnasio. | `\"Av. Constituyentes 1230, Col. Las Américas, CDMX\"` |\n| 30 | `sala_entrenamiento_personal` | `string` | Obligatorio | Nombre de la sala o zona de entrenamiento personal. | `\"Sala PT — Planta Alta\"` |\n| 31 | `zona_horaria` | `string` | Obligatorio | Zona horaria del gimnasio en formato IANA. | `\"America/Mexico_City\"` |\n| 32 | `horario_atencion_inicio` | `string` (formato 24h: `HH:MM`) | Obligatorio | Hora de inicio del horario de atención. | `\"06:00\"` |\n| 33 | `horario_atencion_fin` | `string` (formato 24h: `HH:MM`) | Obligatorio | Hora de fin del horario de atención. | `\"22:00\"` |\n| 34 | `moneda` | `string` | Obligatorio | Código de moneda ISO 4217. | `\"MXN\"` |\n\n---\n\n## Datos del calendario (inyectados desde configuración)\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 35 | `calendario_conectado` | `boolean` | Obligatorio | `true` si hay un calendario conectado y operativo. | `true` |\n| 36 | `calendario_tipo` | `string` (enum) | Obligatorio (si `calendario_conectado` = `true`) | Tipo de calendario conectado. Valores: `google_calendar`, `outlook`, `ical`, `sistema_interno`, `otro`. | `\"sistema_interno\"` |\n| 37 | `dias_calendario_consulta` | `integer` | Obligatorio | Número de días naturales hacia adelante para consultar disponibilidad. Valor por defecto: `7`. | `7` |\n\n---\n\n## Datos del destinatario de notificación interna\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 38 | `responsable_nombre` | `string` | Obligatorio | Nombre del responsable que recibe notificaciones internas. | `\"Carlos Mendoza\"` |\n| 39 | `responsable_telefono` | `string` | Opcional | Teléfono del responsable. | `\"+52 55 2222 3333\"` |\n| 40 | `responsable_email` | `string` | Opcional | Email del responsable. | `\"carlos.mendoza@fitzonepremium.com\"` |\n| 41 | `canal_notificacion_equipo` | `string` (enum) | Obligatorio | Canal para notificar al equipo interno. Valores: `whatsapp_interno`, `email_interno`. | `\"whatsapp_interno\"` |\n\n---\n\n## Configuración de tiempos (inyectada desde configuración)\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 42 | `plazo_respuesta_oferta_horas` | `integer` | Obligatorio | Horas que el usuario tiene para seleccionar un horario. Valor por defecto: `24`. | `24` |\n| 43 | `plazo_contacto_escalamiento_horas` | `integer` | Obligatorio | Horas dentro de las cuales un humano debe contactar al usuario tras escalamiento. Valor por defecto: `4`. | `4` |\n| 44 | `separacion_minima_sesiones_minutos` | `integer` | Obligatorio | Minutos mínimos de separación entre sesiones del mismo entrenador. Valor por defecto: `30`. | `30` |\n\n---\n\n## Campos calculados por el agente\n\nEstos campos **no se reciben de entrada**; el agente los genera internamente.\n\n| # | Nombre del campo | Tipo de dato | Generado en | Descripción |\n|---|---|---|---|---|\n| 45 | `solicitud_id` | `string` | Paso 1 | ID único de la solicitud. Formato: `PTB-YYYYMMDD-HHMMSS-XXX`. |\n| 46 | `fecha_hora_recepcion` | `string` (ISO 8601) | Paso 1 | Marca de tiempo de recepción de la solicitud. |\n| 47 | `tipo_sesion_clasificado` | `string` (enum) | Paso 2 | Tipo de sesión clasificado. |\n| 48 | `entrenador_preferido_extraido` | `string` o `null` | Paso 2 | Nombre del entrenador preferido extraído del mensaje. |\n| 49 | `fecha_preferida_extraida` | `string` o `null` | Paso 2 | Fecha preferida extraída del mensaje. |\n| 50 | `hora_preferida_extraida` | `string` o `null` | Paso 2 | Hora preferida extraída del mensaje. |\n| 51 | `objetivo_extraido` | `string` o `null` | Paso 2 | Objetivo de entrenamiento extraído del mensaje. |\n| 52 | `horarios_disponibles` | `array` de objects | Paso 2 | Lista de horarios disponibles encontrados. |\n| 53 | `horarios_ofrecidos` | `array` de objects | Paso 3 | Los 3 horarios seleccionados para ofrecer. |\n| 54 | `fecha_hora_envio_oferta` | `string` (ISO 8601) o `null` | Paso 3 | Marca de tiempo de envío de la oferta. |\n| 55 | `momento_expiracion_oferta` | `string` (ISO 8601) o `null` | Paso 3 | Momento en que expira la oferta. |\n| 56 | `seleccion_usuario` | `integer` o `null` | Paso 4 | Número de opción seleccionado (1, 2 o 3). |\n| 57 | `reserva_id` | `string` | Paso 4 | ID único de la reserva confirmada. Formato: `PTB-YYYYMMDD-HHMMSS-XXX`. |\n| 58 | `evento_calendario_id` | `string` o `null` | Paso 4 | ID del evento creado en el calendario. |\n| 59 | `fecha_hora_confirmacion` | `string` (ISO 8601) o `null` | Paso 4 | Marca de tiempo de confirmación. |\n| 60 | `fecha_hora_envio_confirmacion` | `string` (ISO 8601) o `null` | Paso 5 | Marca de tiempo de envío de confirmación. |\n| 61 | `estado_envio_confirmacion` | `string` (enum) | Paso 5 | Estado: `generada`, `enviada`, `error_envio`. |\n| 62 | `contacto_es_nuevo` | `boolean` | Paso 6 | `true` si el contacto no existía previamente. |\n| 63 | `fecha_hora_cierre` | `string` (ISO 8601) | Paso 6 | Marca de tiempo de cierre. |\n| 64 | `resultado` | `string` (enum) | Paso 6 | Resultado final. |\n| 65 | `duracion_proceso_segundos` | `integer` | Paso 6 | Duración total del proceso en segundos. |\n\n---\n\n## Validaciones de entrada\n\n| Campo | Regla | Acción si falla |\n|---|---|---|\n| `evento_id` | No vacío, formato válido | Terminar flujo sin ejecutar |\n| `contacto_id` | Debe seguir formato `CON-XXXXXXXX-XXX` | Terminar flujo sin ejecutar |\n| `mensaje_solicitud` | No vacío, mínimo 3 caracteres, máximo 2000 | Terminar flujo sin ejecutar |\n| `canal_origen` | Debe ser uno de los 5 valores válidos | Usar `\"whatsapp\"` como valor por defecto |\n| `numero_remitente` | Entre 8 y 15 dígitos si `canal_origen` es `whatsapp` o `sms` | Escalar a humano (C3.1) |\n| `email_remitente` | Si `canal_origen` es `email`, debe ser formato válido | Escalar a humano (C3.1) |\n| `nombre_contacto` | No vacío, mínimo 2 caracteres | Usar `\"Contacto\"` como valor por defecto |\n| `telefono_contacto` | Entre 8 y 15 dígitos | Terminar flujo sin ejecutar |\n| `tipo_sesion_solicitada` | Debe ser uno de los 6 valores válidos | Si no se puede determinar, ofrecer las 6 opciones para que el usuario elija |\n| `entrenador_preferido` | Si se proporciona, debe existir en `entrenadores_disponibles` | Ofrecer alternativas con otros entrenadores |\n| `fecha_preferida` | Si se proporciona, formato `YYYY-MM-DD`, no anterior a hoy | Ignorar fecha y ofrecer opciones más cercanas |\n| `hora_preferida` | Si se proporciona, formato `HH:MM` | Ignorar hora y ofrecer horarios dentro del horario de atención |\n| `gimnasio_id` | No vacío | Terminar flujo sin ejecutar |\n| `gimnasio_nombre` | No vacío | Terminar flujo sin ejecutar |\n| `gimnasio_telefono` | No vacío, formato válido | Terminar flujo sin ejecutar |\n| `gimnasio_direccion` | No vacío | Terminar flujo sin ejecutar |\n| `sala_entrenamiento_personal` | No vacío | Usar `\"Sala de entrenamiento personal\"` como valor por defecto |\n| `moneda` | Código ISO 4217 válido | Usar `\"MXN\"` como valor por defecto |\n| `calendario_conectado` | Debe ser `true` o `false` | Usar `false` como valor por defecto |\n| `entrenadores_disponibles` | Array con al menos 1 entrenador activo | Escalar a humano (C2.1) |\n| `responsable_nombre` | No vacío | Usar `\"Coordinador de Entrenadores\"` como valor por defecto |\n| `canal_notificacion_equipo` | Debe ser uno de los 2 valores válidos | Usar `\"whatsapp_interno\"` como valor por defecto |\n"
              }
            ]
          }
        ]
      },
      {
        "id": "goal-check-in",
        "carpeta": "goal-check-in-assistant",
        "nombre": "Goal Check-in Assistant",
        "icono": "🎯",
        "descripcion": "Dar seguimiento periódico al progreso de metas fitness del miembro",
        "submodulos": [],
        "categoria": "general",
        "estructura": [
          {
            "id": "goal-check-in-assistant",
            "etiqueta": "",
            "es_plantilla": true,
            "nombre": "goal-check-in-assistant",
            "subcarpetas": [],
            "notas": [
              {
                "id": "goal-check-in-assistant/flujo.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "flujo.md",
                "contenido": "# Flujo de ejecución — Goal Check-in Assistant\n\nEste archivo documenta el flujo fijo de ejecución del agente. Cada ciclo de check-in sigue estos 5 pasos **en orden**, sin pasos condicionales ni bifurcaciones.\n\n---\n\n## Paso 1 — Consulta de la fecha del último check-in registrado\n\nEl agente consulta el sistema del gimnasio para obtener la fecha del último check-in del cliente y determinar si corresponde enviar un nuevo ciclo.\n\n**Acción del agente:**\n1. Recibir el evento disparador `verificar_checkin`.\n2. Extraer los campos de entrada definidos en `variables-entrada.md`.\n3. Registrar la marca de tiempo de recepción en `fecha_hora_recepcion`.\n4. Generar un `checkin_id` único para este ciclo.\n5. Verificar que el `cliente_id` tenga un formato válido.\n6. Verificar que el `estado_cliente` sea `activo` (no suspendido o baja).\n7. Obtener la `fecha_ultimo_checkin` del historial del cliente.\n8. Calcular `dias_desde_ultimo_checkin` = `fecha_actual` – `fecha_ultimo_checkin`. Si no hay check-in previo, usar `fecha_inicio_membresia`.\n9. Obtener los datos del cliente: `nombre_cliente`, `telefono_cliente`, `email_cliente`, `canal_preferido`.\n10. Obtener los datos de la meta: `meta_id`, `meta_tipo`, `meta_descripcion`, `meta_valor_objetivo`, `meta_unidad`, `fecha_inicio_meta`, `fecha_meta`, `valor_inicial`.\n11. Obtener los datos del entrenador: `entrenador_nombre`, `entrenador_telefono`.\n12. Obtener los datos del gimnasio: `gimnasio_nombre`.\n\n**Ejemplo de entrada:**\n\n```json\n{\n  \"evento\": \"verificar_checkin\",\n  \"cliente_id\": \"CLI-20260115-101\",\n  \"contacto_id\": \"CON-20260115-101\",\n  \"nombre_cliente\": \"Ana García López\",\n  \"telefono_cliente\": \"+52 55 9876 5432\",\n  \"email_cliente\": \"ana.garcia@correo.com\",\n  \"canal_preferido\": \"whatsapp\",\n  \"estado_cliente\": \"activo\",\n  \"fecha_ultimo_checkin\": \"2026-08-17\",\n  \"total_checkins_realizados\": 3,\n  \"intervalo_checkin_dias\": 7,\n  \"fecha_inicio_membresia\": \"2026-01-15\",\n  \"meta_id\": \"META-20260301-001\",\n  \"meta_tipo\": \"perdida_peso\",\n  \"meta_descripcion\": \"Perder 10 kg en 6 meses\",\n  \"meta_valor_objetivo\": 65.0,\n  \"meta_unidad\": \"kg\",\n  \"fecha_inicio_meta\": \"2026-03-01\",\n  \"fecha_meta\": \"2026-09-01\",\n  \"valor_inicial\": 75.0,\n  \"peso_actual\": 70.5,\n  \"entrenador_nombre\": \"Coach Martínez\",\n  \"entrenador_telefono\": \"+52 55 2222 3333\",\n  \"fecha_actual\": \"2026-08-24\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\"\n}\n```\n\n---\n\n## Paso 2 — Si se cumplió el intervalo definido, envío de la secuencia fija de preguntas sobre el progreso\n\nEl agente determina si se cumplió el intervalo de check-in y, si es así, genera y envía la secuencia de preguntas.\n\n**Acción del agente:**\n1. Verificar si `dias_desde_ultimo_checkin` ≥ `intervalo_checkin_dias`:\n   - Si es `true` → proceder al envío.\n   - Si es `false` → no enviar check-in. Registrar como `intervalo_no_cumplido`. Terminar flujo.\n2. Calcular `semanas_transcurridas` = (`fecha_actual` – `fecha_inicio_meta`) / 7.\n3. Calcular `semanas_restantes` = (`fecha_meta` – `fecha_actual`) / 7.\n4. Calcular `porcentaje_progreso` = ((`valor_inicial` – `peso_actual`) / (`valor_inicial` – `meta_valor_objetivo`)) × 100 (para metas de pérdida de peso).\n5. Generar el mensaje de check-in usando la plantilla de `formato-salida.md` → sección 1.\n6. Insertar los datos del cliente y la meta en la plantilla.\n7. Enviar el mensaje por el `canal_preferido`.\n8. Si el envío falla, reintentar una sola vez después de 30 segundos.\n9. Si el segundo intento falla, registrar el error (no hay escalamiento para envíos de check-in).\n10. Registrar la marca de tiempo de envío en `fecha_hora_envio`.\n11. Actualizar `estado_checkin` a `enviado`.\n12. Iniciar el contador de plazo de respuesta: **48 horas** desde el envío.\n13. Calcular el `momento_expiracion_respuesta`: `fecha_hora_envio` + 48 horas.\n\n**Tabla de cálculo de progreso por tipo de meta:**\n\n| Tipo de meta | Fórmula de progreso | Ejemplo |\n|---|---|---|\n| `perdida_peso` | ((`valor_inicial` – `peso_actual`) / (`valor_inicial` – `meta_valor_objetivo`)) × 100 | ((75 – 70.5) / (75 – 65)) × 100 = 45% |\n| `ganancia_muscular` | ((`peso_actual` – `valor_inicial`) / (`meta_valor_objetivo` – `valor_inicial`)) × 100 | ((72 – 70) / (75 – 70)) × 100 = 40% |\n| `asistencia` | (`total_clases_asistidas` / `meta_valor_objetivo`) × 100 | (12 / 16) × 100 = 75% |\n| `rendimiento` | ((`valor_actual` – `valor_inicial`) / (`meta_valor_objetivo` – `valor_inicial`)) × 100 | ((100 – 80) / (120 – 80)) × 100 = 50% |\n| `medidas` | ((`valor_inicial` – `valor_actual`) / (`valor_inicial` – `meta_valor_objetivo`)) × 100 | ((90 – 85) / (90 – 75)) × 100 = 33% |\n\n**Ejemplo de cálculo de progreso:**\n\n```json\n{\n  \"checkin_id\": \"GCI-20260824-100000-001\",\n  \"cliente_id\": \"CLI-20260115-101\",\n  \"dias_desde_ultimo_checkin\": 7,\n  \"intervalo_checkin_dias\": 7,\n  \"intervalo_cumplido\": true,\n  \"semanas_transcurridas\": 24,\n  \"semanas_restantes\": 1,\n  \"porcentaje_progreso\": 45,\n  \"progreso_esperado\": 83,\n  \"progreso_por_debajo_esperado\": true\n}\n```\n\n---\n\n## Paso 3 — Registro de las respuestas en el historial del cliente\n\nEl agente registra las respuestas del cliente en su historial una vez que las recibe.\n\n**Acción del agente:**\n1. Esperar la respuesta del cliente dentro del plazo de **48 horas** desde el envío.\n2. Recibir y parsear las 4 respuestas:\n   - `asistencias_semana`: número de asistencias (0–7).\n   - `peso_actual_kg`: peso en kg con 1 decimal.\n   - `satisfaccion_progreso`: satisfacción en escala 1–5.\n   - `dificultades_reportadas`: texto libre o \"Ninguna\".\n3. Si el cliente responde parcialmente (menos de 4 respuestas), registrar lo recibido y reintentar las preguntas faltantes en 24 horas.\n4. Si el cliente no responde en 48 horas, registrar como `sin_respuesta` y esperar al próximo ciclo.\n5. Guardar las respuestas en el historial del cliente:\n   - `checkin_id`: ID del check-in.\n   - `fecha_checkin`: fecha del check-in.\n   - `respuestas`: objeto con las 4 respuestas.\n   - `texto_respuesta_completo`: texto completo de la respuesta del cliente.\n6. Actualizar `estado_checkin` a `respondido`, `parcial` o `sin_respuesta`.\n\n**Tabla de clasificación de respuestas:**\n\n| Condición | Clasificación | Acción |\n|---|---|---|\n| 4 respuestas recibidas | `respondido` | Proceder al paso 4 |\n| 1–3 respuestas recibidas | `parcial` | Reintentar preguntas faltantes en 24h |\n| Sin respuesta en 48 horas | `sin_respuesta` | Registrar. Esperar próximo ciclo |\n| Dificultad grave indicada | `dificultad_grave` | Registrar. Notificar al entrenador inmediatamente |\n| Meta lograda indicada | `meta_lograda` | Registrar. Notificar al entrenador para evaluación final |\n\n---\n\n## Paso 4 — Comparación del progreso contra la meta establecida\n\nEl agente compara los datos del check-in actual contra la meta establecida y determina si el progreso está en curso, adelantado o por debajo de lo esperado.\n\n**Acción del agente:**\n1. Obtener el `peso_actual_kg` del check-in actual.\n2. Calcular el `progreso_actual_porcentaje` según la fórmula del tipo de meta (ver tabla del paso 2).\n3. Calcular el `progreso_esperado_porcentaje` = (`semanas_transcurridas` / `semanas_totales_meta`) × 100.\n4. Comparar `progreso_actual_porcentaje` contra `progreso_esperado_porcentaje`:\n   - Si `progreso_actual_porcentaje` ≥ `progreso_esperado_porcentaje` → `adelantado`.\n   - Si `progreso_actual_porcentaje` ≥ `progreso_esperado_porcentaje` × 0.8 → `en_curso`.\n   - Si `progreso_actual_porcentaje` < `progreso_esperado_porcentaje` × 0.8 → `por_debajo`.\n   - Si `progreso_actual_porcentaje` ≤ 0 y `semanas_transcurridas` > 2 → `estancado`.\n5. Calcular `diferencia_progreso` = `progreso_actual_porcentaje` – `progreso_esperado_porcentaje`.\n6. Generar el `estado_progreso` con la clasificación obtenida.\n7. Calcular `peso_necesario_para_meta` = `peso_actual_kg` – ((`meta_valor_objetivo` – `peso_actual_kg`) / `semanas_restantes`) (para metas de pérdida de peso).\n\n**Tabla de estados de progreso:**\n\n| Estado | Condición | Emoji | Descripción |\n|---|---|---|---|\n| `adelantado` | `progreso_actual` ≥ `progreso_esperado` | 🟢 | El cliente va por delante de lo esperado |\n| `en_curso` | `progreso_actual` ≥ `progreso_esperado` × 0.8 | 🟡 | El cliente va dentro del rango esperado |\n| `por_debajo` | `progreso_actual` < `progreso_esperado` × 0.8 | 🟠 | El cliente va por debajo de lo esperado |\n| `estancado` | `progreso_actual` ≤ 0 y `semanas_transcurridas` > 2 | 🔴 | Sin progreso visible después de 2+ semanas |\n\n**Ejemplo de comparación:**\n\n```json\n{\n  \"checkin_id\": \"GCI-20260824-100000-001\",\n  \"cliente_id\": \"CLI-20260115-101\",\n  \"peso_actual_kg\": 70.5,\n  \"peso_inicial_kg\": 75.0,\n  \"peso_objetivo_kg\": 65.0,\n  \"progreso_actual_porcentaje\": 45,\n  \"progreso_esperado_porcentaje\": 83,\n  \"diferencia_progreso\": -38,\n  \"estado_progreso\": \"por_debajo\",\n  \"semanas_transcurridas\": 24,\n  \"semanas_totales_meta\": 26,\n  \"semanas_restantes\": 2,\n  \"peso_necesario_para_meta\": 62.75,\n  \"notificar_humano\": true,\n  \"motivo_notificacion\": \"Progreso 38% por debajo de lo esperado. Solo alcanzó 45% del objetivo con 83% del tiempo transcurrido.\"\n}\n```\n\n---\n\n## Paso 5 — Notificación al responsable humano si el progreso está por debajo de lo esperado\n\nEl agente notifica al entrenador responsable cuando el progreso del cliente está por debajo de lo esperado, para que pueda ajustar el plan de entrenamiento o nutrición.\n\n**Acción del agente:**\n1. Verificar si `notificar_humano` es `true` en el paso 4.\n2. Si es `true`, generar la notificación usando la plantilla de `formato-salida.md` → sección 2.\n3. Incluir en la notificación:\n   - Datos del cliente (nombre, meta, progreso actual).\n   - Comparación: progreso actual vs esperado.\n   - Respuestas del check-in (asistencia, peso, satisfacción, dificultades).\n   - Acción sugerida para el entrenador.\n4. Enviar la notificación por `canal_notificacion_equipo`.\n5. Si el envío falla, reintentar una sola vez después de 30 segundos.\n6. Si el segundo intento falla, registrar el error.\n7. Registrar la marca de tiempo de notificación en `fecha_hora_notificacion`.\n8. Generar el registro completo del check-in con el formato de `formato-salida.md` → sección 4 (registro JSON).\n9. Registrar la marca de tiempo de cierre en `fecha_hora_cierre`.\n10. Calcular `duracion_proceso_segundos` = `fecha_hora_cierre` - `fecha_hora_recepcion`.\n11. Guardar el registro en el sistema de historial del cliente.\n12. Actualizar `fecha_ultimo_checkin` en el perfil del cliente.\n\n**Tabla de plazos de notificación por estado de progreso:**\n\n| Estado de progreso | Notificar al entrenador | Plazo de seguimiento |\n|---|---|---|\n| `adelantado` | No (solo registrar) | Próximo check-in |\n| `en_curso` | No (solo registrar) | Próximo check-in |\n| `por_debajo` | Sí | 48 horas |\n| `estancado` | Sí + copia al gerente | 24 horas |\n\n**Tabla de resultados posibles:**\n\n| Resultado | Descripción | Próxima acción del sistema |\n|---|---|---|\n| `adelantado` | Progreso por delante de lo esperado | Registrar. Próximo check-in en `intervalo_checkin_dias`. |\n| `en_curso` | Progreso dentro del rango esperado | Registrar. Próximo check-in en `intervalo_checkin_dias`. |\n| `por_debajo` | Progreso por debajo de lo esperado | Notificar entrenador. Seguimiento en 48h. |\n| `estancado` | Sin progreso visible | Notificar entrenador + gerente. Seguimiento en 24h. |\n| `meta_lograda` | Cliente indicó que logró su meta | Notificar entrenador para evaluación final. |\n| `dificultad_grave` | Cliente reportó dificultad médica | Notificar entrenador inmediatamente. |\n| `sin_respuesta` | Cliente no respondió al check-in | Registrar. Esperar próximo ciclo. |\n| `parcial` | Respuesta incompleta | Reintentar preguntas faltantes en 24h. |\n| `intervalo_no_cumplido` | Aún no se cumplió el intervalo | No hacer nada. Verificar en próximo ciclo. |\n"
              },
              {
                "id": "goal-check-in-assistant/formato-salida.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "formato-salida.md",
                "contenido": "# Formato de salida — Goal Check-in Assistant\n\nEste archivo define la estructura exacta de los mensajes y registros que el agente debe producir. No se permite desviarse de estas plantillas.\n\n---\n\n## 1. Mensaje de check-in al cliente\n\nSe genera en el paso 2 del flujo. Se envía por el canal configurado (`canal_preferido`).\n\n### Plantilla — Check-in semanal (whatsapp/sms)\n\n```\n¡Hola {{nombre_cliente}}! 💪 Es momento de tu check-in de progreso.\n\n🎯 **Tu meta:** {{meta_descripcion}}\n📅 **Meta establecida:** {{fecha_meta_formateada}}\n⏱️ **Semanas restantes:** {{semanas_restantes}}\n\nPor favor, responde con los siguientes datos:\n\n1️⃣ **¿Cuántas veces asististe al gimnasio esta semana?** (número del 0 al 7)\n2️⃣ **¿Cuánto pesas actualmente?** (en kg, ejemplo: 70.5)\n3️⃣ **¿Cómo te sientes con tu progreso?** (1-5, donde 1 = muy mal, 5 = excelente)\n4️⃣ **¿Alguna dificultad o limitación esta semana?** (texto libre o escribe \"Ninguna\")\n\n¡Cada semana cuenta! 💪\n— {{gimnasio_nombre}}\n```\n\n### Plantilla — Check-in semanal (email)\n\n**Asunto:** Tu check-in de progreso semanal — {{gimnasio_nombre}}\n\n```\nHola {{nombre_cliente}},\n\nEs momento de tu check-in de progreso en {{gimnasio_nombre}}.\n\n🎯 **Tu meta:** {{meta_descripcion}}\n📅 **Meta establecida:** {{fecha_meta_formateada}}\n⏱️ **Semanas restantes:** {{semanas_restantes}}\n\nPor favor, responde este correo con los siguientes datos:\n\n1️⃣ **¿Cuántas veces asististe al gimnasio esta semana?** (número del 0 al 7)\n2️⃣ **¿Cuánto pesas actualmente?** (en kg, ejemplo: 70.5)\n3️⃣ **¿Cómo te sientes con tu progreso?** (1-5, donde 1 = muy mal, 5 = excelente)\n4️⃣ **¿Alguna dificultad o limitación esta semana?** (texto libre o escribe \"Ninguna\")\n\n¡Cada semana cuenta! 💪\n— {{gimnasio_nombre}}\n```\n\n### Plantilla — Check-in semanal (push)\n\n**Título:** 💪 Check-in de progreso\n**Cuerpo:** Responde 4 preguntas para registrar tu avance. Toca para completar.\n\n### Ejemplo renderizado (whatsapp — check-in semanal)\n\n> ¡Hola Ana García López! 💪 Es momento de tu check-in de progreso.\n>\n> 🎯 **Tu meta:** Perder 10 kg en 6 meses\n> 📅 **Meta establecida:** 1 de septiembre de 2026\n> ⏱️ **Semanas restantes:** 1\n>\n> Por favor, responde con los siguientes datos:\n>\n> 1️⃣ **¿Cuántas veces asististe al gimnasio esta semana?** (número del 0 al 7)\n> 2️⃣ **¿Cuánto pesas actualmente?** (en kg, ejemplo: 70.5)\n> 3️⃣ **¿Cómo te sientes con tu progreso?** (1-5, donde 1 = muy mal, 5 = excelente)\n> 4️⃣ **¿Alguna dificultad o limitación esta semana?** (texto libre o escribe \"Ninguna\")\n>\n> ¡Cada semana cuenta! 💪\n> — FitZone Premium\n\n---\n\n## 2. Notificación al entrenador — Progreso por debajo de lo esperado\n\nSe genera en el paso 5 del flujo. Se envía al entrenador responsable por el canal configurado (`canal_notificacion_equipo`).\n\n### Plantilla (whatsapp_interno)\n\n```\n📊 CHECK-IN — PROGRESO POR DEBAJO DE LO ESPERADO\n==========================================\n\n**Cliente:** {{nombre_cliente}}\n**Meta:** {{meta_descripcion}}\n**Entrenador responsable:** {{entrenador_nombre}}\n\n📈 **Progreso actual:** {{progreso_actual_porcentaje}}%\n📈 **Progreso esperado:** {{progreso_esperado_porcentaje}}%\n📉 **Diferencia:** {{diferencia_progreso}} puntos porcentuales\n\n📋 **Respuestas del check-in:**\n• Asistencias esta semana: {{asistencias_semana}} veces\n• Peso actual: {{peso_actual_kg}} kg (inicial: {{valor_inicial}} kg)\n• Satisfacción: {{satisfaccion_progreso}}/5\n• Dificultades: {{dificultades_reportadas}}\n\n🎯 **Acción sugerida:** {{accion_sugerida}}\n⏱️ **Plazo de seguimiento:** {{plazo_seguimiento}} horas\n\n🔗 Check-in: {{checkin_id}}\n🔗 Meta: {{meta_id}}\n```\n\n### Plantilla (email_interno)\n\n**Asunto:** Check-in — {{nombre_cliente}} — Progreso por debajo — {{gimnasio_nombre}}\n\n```\nCheck-in de progreso con resultado por debajo de lo esperado en {{gimnasio_nombre}}.\n\nCliente: {{nombre_cliente}}\nMeta: {{meta_descripcion}}\nEntrenador responsable: {{entrenador_nombre}}\n\nProgreso actual: {{progreso_actual_porcentaje}}%\nProgreso esperado: {{progreso_esperado_porcentaje}}%\nDiferencia: {{diferencia_progreso}} puntos porcentuales\n\nRespuestas del check-in:\n• Asistencias esta semana: {{asistencias_semana}} veces\n• Peso actual: {{peso_actual_kg}} kg (inicial: {{valor_inicial}} kg)\n• Satisfacción: {{satisfaccion_progreso}}/5\n• Dificultades: {{dificultades_reportadas}}\n\nAcción sugerida: {{accion_sugerida}}\nPlazo de seguimiento: {{plazo_seguimiento}} horas\n\nCheck-in: {{checkin_id}}\nMeta: {{meta_id}}\n```\n\n### Plantilla — Notificación de estancamiento (copia al gerente)\n\n**Asunto:** 🔴 ESTANCAMIENTO — {{nombre_cliente}} — {{meta_descripcion}} — {{gimnasio_nombre}}\n\n```\nALERTA: Estancamiento de progreso detectado en {{gimnasio_nombre}}.\n\nCliente: {{nombre_cliente}}\nMeta: {{meta_descripcion}}\nEntrenador: {{entrenador_nombre}}\n\nEstado: ESTANCADO — Sin progreso visible después de {{semanas_sin_progreso}} semanas.\nProgreso actual: {{progreso_actual_porcentaje}}%\nSemanas transcurridas: {{semanas_transcurridas}}/{{semanas_totales_meta}}\n\nAcción requerida: Revisión urgente del plan de entrenamiento y/o nutrición.\nPlazo de seguimiento: {{plazo_seguimiento_estancado}} horas\n\nCheck-in: {{checkin_id}}\nMeta: {{meta_id}}\n```\n\n### Ejemplo renderizado (whatsapp_interno — progreso por debajo)\n\n> 📊 CHECK-IN — PROGRESO POR DEBAJO DE LO ESPERADO\n> ==========================================\n>\n> **Cliente:** Ana García López\n> **Meta:** Perder 10 kg en 6 meses\n> **Entrenador responsable:** Coach Martínez\n>\n> 📈 **Progreso actual:** 45%\n> 📈 **Progreso esperado:** 83%\n> 📉 **Diferencia:** -38 puntos porcentuales\n>\n> 📋 **Respuestas del check-in:**\n> • Asistencias esta semana: 2 veces\n> • Peso actual: 70.5 kg (inicial: 75.0 kg)\n> • Satisfacción: 3/5\n> • Dificultades: \"Trabajo muchas horas, me cuesta llegar\"\n>\n> 🎯 **Acción sugerida:** Revisar disponibilidad horaria del cliente. Considerar ajustar plan de entrenamiento a horarios más accesibles. Evaluar si la meta de 65 kg es realista en 1 semana.\n> ⏱️ **Plazo de seguimiento:** 48 horas\n>\n> 🔗 Check-in: GCI-20260824-100000-001\n> 🔗 Meta: META-20260301-001\n\n---\n\n## 3. Mensaje de recordatorio — Sin respuesta al check-in\n\nSe envía si el cliente no responde en 24 horas.\n\n### Plantilla (whatsapp/sms)\n\n```\n¡Hola {{nombre_cliente}}! 💪 Te recordamos que tu check-in de progreso está pendiente.\n\n🎯 **Tu meta:** {{meta_descripcion}}\n⏱️ **Semanas restantes:** {{semanas_restantes}}\n\nResponde con tus datos cuando puedas:\n1️⃣ Asistencias esta semana\n2️⃣ Peso actual (kg)\n3️⃣ Satisfacción (1-5)\n4️⃣ Dificultades (o \"Ninguna\")\n\n¡Nos importa tu progreso! 💪\n— {{gimnasio_nombre}}\n```\n\n### Ejemplo renderizado (whatsapp — recordatorio)\n\n> ¡Hola Ana García López! 💪 Te recordamos que tu check-in de progreso está pendiente.\n>\n> 🎯 **Tu meta:** Perder 10 kg en 6 meses\n> ⏱️ **Semanas restantes:** 1\n>\n> Responde con tus datos cuando puedas:\n> 1️⃣ Asistencias esta semana\n> 2️⃣ Peso actual (kg)\n> 3️⃣ Satisfacción (1-5)\n> 4️⃣ Dificultades (o \"Ninguna\")\n>\n> ¡Nos importa tu progreso! 💪\n> — FitZone Premium\n\n---\n\n## 4. Registro de check-in completo (JSON)\n\nSe genera al finalizar el paso 5 del flujo para cada check-in procesado.\n\n```json\n{\n  \"registro_id\": \"GCIREG-YYYYMMDD-HHMMSS-XXX\",\n  \"checkin_id\": \"GCI-YYYYMMDD-HHMMSS-XXX\",\n  \"cliente_id\": \"CLI-YYYYMMDD-XXX\",\n  \"contacto_id\": \"CON-YYYYMMDD-XXX\",\n  \"gimnasio_id\": \"string\",\n  \"gimnasio_nombre\": \"string\",\n  \"nombre_cliente\": \"string\",\n  \"telefono_cliente\": \"string\",\n  \"email_cliente\": \"string | null\",\n  \"canal_preferido\": \"whatsapp | sms | email | app\",\n  \"meta_id\": \"META-YYYYMMDD-XXX\",\n  \"meta_tipo\": \"perdida_peso | ganancia_muscular | asistencia | rendimiento | medidas\",\n  \"meta_descripcion\": \"string\",\n  \"meta_valor_objetivo\": 0,\n  \"meta_unidad\": \"string\",\n  \"valor_inicial\": 0,\n  \"entrenador_nombre\": \"string\",\n  \"intervalo_checkin_dias\": 7 | 14 | 30,\n  \"fecha_hora_recepcion\": \"YYYY-MM-DDTHH:MM:SS\",\n  \"dias_desde_ultimo_checkin\": 0,\n  \"intervalo_cumplido\": true | false,\n  \"semanas_transcurridas\": 0,\n  \"semanas_totales_meta\": 0,\n  \"semanas_restantes\": 0,\n  \"fecha_hora_envio\": \"YYYY-MM-DDTHH:MM:SS | null\",\n  \"estado_checkin\": \"enviado | respondido | parcial | sin_respuesta | dificultad_grave | meta_lograda | intervalo_no_cumplido\",\n  \"asistencias_semana\": 0 | null,\n  \"peso_actual_kg\": 0.0 | null,\n  \"satisfaccion_progreso\": 0 | null,\n  \"dificultades_reportadas\": \"string | null\",\n  \"fecha_hora_respuesta\": \"YYYY-MM-DDTHH:MM:SS | null\",\n  \"progreso_actual_porcentaje\": 0,\n  \"progreso_esperado_porcentaje\": 0,\n  \"diferencia_progreso\": 0,\n  \"estado_progreso\": \"adelantado | en_curso | por_debajo | estancado\",\n  \"peso_necesario_para_meta\": 0.0 | null,\n  \"notificar_humano\": true | false,\n  \"motivo_notificacion\": \"string | null\",\n  \"fecha_hora_notificacion\": \"YYYY-MM-DDTHH:MM:SS | null\",\n  \"resultado\": \"adelantado | en_curso | por_debajo | estancado | meta_lograda | dificultad_grave | sin_respuesta | parcial | intervalo_no_cumplido\",\n  \"fecha_hora_cierre\": \"YYYY-MM-DDTHH:MM:SS\",\n  \"duracion_proceso_segundos\": 0,\n  \"agente\": \"goal-check-in-assistant\"\n}\n```\n\n### Ejemplo — Progreso por debajo, notificado al entrenador\n\n```json\n{\n  \"registro_id\": \"GCIREG-20260824-100005-001\",\n  \"checkin_id\": \"GCI-20260824-100000-001\",\n  \"cliente_id\": \"CLI-20260115-101\",\n  \"contacto_id\": \"CON-20260115-101\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"nombre_cliente\": \"Ana García López\",\n  \"telefono_cliente\": \"+52 55 9876 5432\",\n  \"email_cliente\": \"ana.garcia@correo.com\",\n  \"canal_preferido\": \"whatsapp\",\n  \"meta_id\": \"META-20260301-001\",\n  \"meta_tipo\": \"perdida_peso\",\n  \"meta_descripcion\": \"Perder 10 kg en 6 meses\",\n  \"meta_valor_objetivo\": 65.0,\n  \"meta_unidad\": \"kg\",\n  \"valor_inicial\": 75.0,\n  \"entrenador_nombre\": \"Coach Martínez\",\n  \"intervalo_checkin_dias\": 7,\n  \"fecha_hora_recepcion\": \"2026-08-24T08:00:00\",\n  \"dias_desde_ultimo_checkin\": 7,\n  \"intervalo_cumplido\": true,\n  \"semanas_transcurridas\": 24,\n  \"semanas_totales_meta\": 26,\n  \"semanas_restantes\": 2,\n  \"fecha_hora_envio\": \"2026-08-24T10:00:02\",\n  \"estado_checkin\": \"respondido\",\n  \"asistencias_semana\": 2,\n  \"peso_actual_kg\": 70.5,\n  \"satisfaccion_progreso\": 3,\n  \"dificultades_reportadas\": \"Trabajo muchas horas, me cuesta llegar\",\n  \"fecha_hora_respuesta\": \"2026-08-24T14:30:00\",\n  \"progreso_actual_porcentaje\": 45,\n  \"progreso_esperado_porcentaje\": 83,\n  \"diferencia_progreso\": -38,\n  \"estado_progreso\": \"por_debajo\",\n  \"peso_necesario_para_meta\": 62.75,\n  \"notificar_humano\": true,\n  \"motivo_notificacion\": \"Progreso 38% por debajo de lo esperado. Solo alcanzó 45% del objetivo con 83% del tiempo transcurrido.\",\n  \"fecha_hora_notificacion\": \"2026-08-24T14:30:05\",\n  \"resultado\": \"por_debajo\",\n  \"fecha_hora_cierre\": \"2026-08-24T14:30:07\",\n  \"duracion_proceso_segundos\": 23407,\n  \"agente\": \"goal-check-in-assistant\"\n}\n```\n\n### Ejemplo — Progreso en curso, sin notificación\n\n```json\n{\n  \"registro_id\": \"GCIREG-20260824-100005-002\",\n  \"checkin_id\": \"GCI-20260824-100000-002\",\n  \"cliente_id\": \"CLI-20260301-205\",\n  \"contacto_id\": \"CON-20260301-205\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"nombre_cliente\": \"Laura Hernández\",\n  \"telefono_cliente\": \"+52 55 1122 3344\",\n  \"email_cliente\": null,\n  \"canal_preferido\": \"whatsapp\",\n  \"meta_id\": \"META-20260601-002\",\n  \"meta_tipo\": \"ganancia_muscular\",\n  \"meta_descripcion\": \"Ganar 5 kg de masa muscular en 6 meses\",\n  \"meta_valor_objetivo\": 70.0,\n  \"meta_unidad\": \"kg\",\n  \"valor_inicial\": 65.0,\n  \"entrenador_nombre\": \"Coach Herrera\",\n  \"intervalo_checkin_dias\": 7,\n  \"fecha_hora_recepcion\": \"2026-08-24T08:00:00\",\n  \"dias_desde_ultimo_checkin\": 7,\n  \"intervalo_cumplido\": true,\n  \"semanas_transcurridas\": 10,\n  \"semanas_totales_meta\": 26,\n  \"semanas_restantes\": 16,\n  \"fecha_hora_envio\": \"2026-08-24T10:00:02\",\n  \"estado_checkin\": \"respondido\",\n  \"asistencias_semana\": 4,\n  \"peso_actual_kg\": 67.0,\n  \"satisfaccion_progreso\": 4,\n  \"dificultades_reportadas\": \"Ninguna\",\n  \"fecha_hora_respuesta\": \"2026-08-24T11:00:00\",\n  \"progreso_actual_porcentaje\": 40,\n  \"progreso_esperado_porcentaje\": 38,\n  \"diferencia_progreso\": 2,\n  \"estado_progreso\": \"en_curso\",\n  \"peso_necesario_para_meta\": null,\n  \"notificar_humano\": false,\n  \"motivo_notificacion\": null,\n  \"fecha_hora_notificacion\": null,\n  \"resultado\": \"en_curso\",\n  \"fecha_hora_cierre\": \"2026-08-24T11:00:03\",\n  \"duracion_proceso_segundos\": 10803,\n  \"agente\": \"goal-check-in-assistant\"\n}\n```\n\n---\n\n## 5. Registro de escalamiento a humano (JSON)\n\nSe genera cuando se activa una condición de escalamiento (ver `reglas-escalamiento.md`).\n\n```json\n{\n  \"registro_id\": \"GCIREG-YYYYMMDD-HHMMSS-XXX\",\n  \"checkin_id\": \"GCI-YYYYMMDD-HHMMSS-XXX\",\n  \"evento\": \"escalamiento\",\n  \"fecha_hora_ejecucion\": \"YYYY-MM-DDTHH:MM:SS\",\n  \"agente\": \"goal-check-in-assistant\",\n  \"gimnasio_id\": \"string\",\n  \"gimnasio_nombre\": \"string\",\n  \"nombre_cliente\": \"string\",\n  \"telefono_cliente\": \"string\",\n  \"email_cliente\": \"string | null\",\n  \"canal_preferido\": \"whatsapp | sms | email | app\",\n  \"meta_id\": \"string\",\n  \"meta_descripcion\": \"string\",\n  \"resultado\": \"escalamiento\",\n  \"detalles\": \"Motivo: {{motivo_escalamiento}}.\",\n  \"responsable_escalamiento\": \"string nombre del responsable\",\n  \"canal_escalamiento\": \"whatsapp_interno | email_interno\",\n  \"plazo_respuesta_horas\": 0,\n  \"datos_origen\": {\n    \"checkin_id\": \"{{checkin_id}}\",\n    \"cliente_id\": \"{{cliente_id}}\",\n    \"nombre_cliente\": \"{{nombre_cliente}}\",\n    \"meta_descripcion\": \"{{meta_descripcion}}\",\n    \"progreso_actual_porcentaje\": 0,\n    \"progreso_esperado_porcentaje\": 0\n  }\n}\n```\n\n### Ejemplo — Dificultad grave reportada\n\n```json\n{\n  \"registro_id\": \"GCIREG-20260824-160010-003\",\n  \"checkin_id\": \"GCI-20260824-150000-003\",\n  \"evento\": \"escalamiento\",\n  \"fecha_hora_ejecucion\": \"2026-08-24T16:00:10\",\n  \"agente\": \"goal-check-in-assistant\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"nombre_cliente\": \"Roberto Ruiz Méndez\",\n  \"telefono_cliente\": \"+52 55 4567 8901\",\n  \"email_cliente\": \"roberto.ruiz@correo.com\",\n  \"canal_preferido\": \"whatsapp\",\n  \"meta_id\": \"META-20260701-003\",\n  \"meta_descripcion\": \"Ganar 3 kg de masa muscular en 3 meses\",\n  \"resultado\": \"escalamiento\",\n  \"detalles\": \"Motivo: Cliente reportó dificultad grave: 'Me lastimé la espalda entrenando ayer, no puedo hacer ejercicios de espalda ni piernas'. Requiere contacto inmediato del entrenador para ajuste de plan y posible derivación médica.\",\n  \"responsable_escalamiento\": \"Coach Martínez\",\n  \"canal_escalamiento\": \"whatsapp_interno\",\n  \"plazo_respuesta_horas\": 2,\n  \"datos_origen\": {\n    \"checkin_id\": \"GCI-20260824-150000-003\",\n    \"cliente_id\": \"CLI-20260701-301\",\n    \"nombre_cliente\": \"Roberto Ruiz Méndez\",\n    \"meta_descripcion\": \"Ganar 3 kg de masa muscular en 3 meses\",\n    \"progreso_actual_porcentaje\": 30,\n    \"progreso_esperado_porcentaje\": 65\n  }\n}\n```\n"
              },
              {
                "id": "goal-check-in-assistant/prompt-sistema.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "prompt-sistema.md",
                "contenido": "# Prompt del sistema — Goal Check-in Assistant\n\n## Identidad\nEres **FitBot**, asistente virtual de **GymPro Fitness Center**. Tu función es dar seguimiento periódico al progreso del cliente hacia sus metas de fitness, enviando encuestas de progreso y comparando resultados contra los objetivos establecidos.\n\n## Tono de comunicación\n- **Motivador y empático**: el cliente está en un proceso de transformación. Reconocer sus esfuerzos, incluso cuando el progreso es lento.\n- **Orientado a datos**: cada pregunta es específica y medible. No preguntar \"¿Cómo vas?\" sino \"¿Cuántas veces asististe esta semana?\".\n- **Sin juicio**: si el progreso es bajo, no criticar. Ofrecer perspectiva y sugerir acciones concretas.\n- **Breve pero completo**: máximo 4 párrafos por mensaje. Las preguntas deben ser claras y directas.\n- **Personalizado**: usar el nombre del cliente. Referenciar su meta específica.\n\n## Flujo de ejecución\nEjecuta los siguientes pasos en orden exacto, sin omitir ni reordenar:\n\n### Paso 1 — Consulta de la fecha del último check-in registrado\nConsulta el sistema del gimnasio para obtener la fecha del último check-in del cliente y determinar si corresponde enviar un nuevo ciclo.\n\n### Paso 2 — Si se cumplió el intervalo definido, envío de la secuencia fija de preguntas sobre el progreso\nDetermina si se cumplió el intervalo de check-in y, si es así, genera y envía la secuencia de preguntas sobre el progreso del cliente.\n\n### Paso 3 — Registro de las respuestas en el historial del cliente\nRegistra las respuestas del cliente en su historial una vez que las recibe.\n\n### Paso 4 — Comparación del progreso contra la meta establecida\nCompara los datos del check-in actual contra la meta establecida y determina si el progreso está en curso, adelantado o por debajo de lo esperado.\n\n### Paso 5 — Notificación al responsable humano si el progreso está por debajo de lo esperado\nNotifica al entrenador responsable cuando el progreso del cliente está por debajo de lo esperado, para que pueda ajustar el plan de entrenamiento o nutrición.\n\n## Ejemplo de ejecución\n\n**Entrada:**\n- nombre_cliente: \"Ana García López\"\n- meta: \"Perder 10 kg en 6 meses\"\n- peso_inicial: 75 kg\n- peso_actual: 70.5 kg\n- semanas_transcurridas: 24 de 26\n\n**Salida generada (paso 2 — encuesta de progreso):**\n```\n¡Hola Ana García! 💪 Es momento de tu check-in semanal de progreso.\n\n🎯 Tu meta: Perder 10 kg en 6 meses\n📅 Meta establecida: 1 de septiembre de 2026\n⏱️ Semanas restantes: 2\n\nPor favor, responde con los siguientes datos:\n\n1️⃣ ¿Cuántas veces asististe al gimnasio esta semana? (número)\n2️⃣ ¿Cuánto pesas actualmente? (kg)\n3️⃣ ¿Cómo te sientes con tu progreso? (1-5)\n4️⃣ ¿Alguna dificultad o limitación esta semana? (texto libre o \"Ninguna\")\n\n¡Cada semana cuenta! 💪\n```\n\n## Secuencia de check-in (4 preguntas fijas)\n\n| # | Pregunta | Tipo de respuesta | Campo de registro |\n|---|---|---|---|\n| 1 | ¿Cuántas veces asististe al gimnasio esta semana? | `integer` (0–7) | `asistencias_semana` |\n| 2 | ¿Cuánto pesas actualmente? | `number` (kg, con 1 decimal) | `peso_actual_kg` |\n| 3 | ¿Cómo te sientes con tu progreso? | `integer` (1–5) | `satisfaccion_progreso` |\n| 4 | ¿Alguna dificultad o limitación esta semana? | `string` (texto libre o \"Ninguna\") | `dificultades_reportadas` |\n\n## Intervalos de check-in\n\n| ID | Intervalo | Frecuencia | Descripción |\n|---|---|---|---|\n| `CHECKIN_SEMANAL` | Cada 7 días | Semanal | Para clientes en fase activa de entrenamiento |\n| `CHECKIN_QUINCENAL` | Cada 14 días | Quincenal | Para clientes en fase de mantenimiento |\n| `CHECKIN_MENSUAL` | Cada 30 días | Mensual | Para clientes en fase de evaluación inicial o avanzada |\n\n## Restricciones\n- No enviar check-ins fuera del horario de atención (antes de las 08:00 o después de las 22:00).\n- No comparar datos de diferentes clientes. Cada check-in es independiente.\n- Las 4 preguntas se envían en una sola secuencia, no una por una.\n- No inventar datos. Si algún campo no está disponible, usar \"N/D\" y continuar.\n\n## Reglas de escalamiento\n- Si el progreso está por debajo de lo esperado → notificar al entrenador.\n- Si el cliente indica una dificultad grave (lesión, enfermedad) → notificar al entrenador inmediatamente.\n- Si el cliente indica que ya logró su meta → notificar al entrenador para evaluación final.\n- Si el envío del check-in falla 2 veces consecutivas → escalar a humano.\n- Tiempo máximo de respuesta: 2 minutos. Si excede, escalar a equipo humano.\n"
              },
              {
                "id": "goal-check-in-assistant/reglas-escalamiento.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "reglas-escalamiento.md",
                "contenido": "# Reglas de escalamiento — Goal Check-in Assistant\n\nEste archivo define la lista cerrada y exacta de condiciones bajo las cuales el agente **debe** notificar al responsable humano (entrenador personal, coordinador o gerente). No existe discrecionalidad: si se cumple alguna condición, la notificación es **obligatoria**.\n\n---\n\n## Condiciones de notificación\n\n### C1 — Progreso por debajo de lo esperado\n\n| # | Condición | Acción |\n|---|---|---|\n| C1.1 | `progreso_actual_porcentaje` < `progreso_esperado_porcentaje` × 0.8 | Notificar al entrenador. Motivo: `Progreso {{diferencia_progreso}} puntos por debajo de lo esperado. Solo alcanzó {{progreso_actual_porcentaje}}% del objetivo con {{progreso_esperado_porcentaje}}% del tiempo transcurrido.` Plazo: **48 horas**. |\n| C1.2 | `progreso_actual_porcentaje` ≤ 0 y `semanas_transcurridas` > 2 | Notificar al entrenador + gerente (estancamiento). Motivo: `Sin progreso visible después de {{semanas_transcurridas}} semanas. Progreso actual: {{progreso_actual_porcentaje}}%. Revisión urgente del plan de entrenamiento y/o nutrición.` Plazo: **24 horas**. |\n| C1.3 | `diferencia_progreso` < -30 puntos porcentuales (progreso muy por debajo) | Notificar al entrenador + gerente. Motivo: `Progreso extremadamente por debajo: {{progreso_actual_porcentaje}}% vs {{progreso_esperado_porcentaje}}% esperado (diferencia: {{diferencia_progreso}} puntos). Requiere intervención inmediata.` Plazo: **24 horas**. |\n\n### C2 — Satisfacción baja del cliente\n\n| # | Condición | Acción |\n|---|---|---|\n| C2.1 | `satisfaccion_progreso` ≤ 2 (muy insatisfecho) | Notificar al entrenador. Motivo: `Cliente reporta baja satisfacción con su progreso ({{satisfaccion_progreso}}/5). Requiere contacto del entrenador para motivar y ajustar expectativas.` Plazo: **48 horas**. |\n| C2.2 | `satisfaccion_progreso` ≤ 2 por **3 check-ins consecutivos** | Notificar al entrenador + gerente. Motivo: `Cliente reporta baja satisfacción (≤2/5) por 3 check-ins consecutivos. Riesgo de deserción por falta de resultados percibidos.` Plazo: **24 horas**. |\n\n### C3 — Dificultades reportadas\n\n| # | Condición | Acción |\n|---|---|---|\n| C3.1 | El cliente indica una dificultad grave (contiene palabras clave: \"lastimé\", \"lesión\", \"dolor\", \"enfermo\", \"no puedo caminar\", \"no puedo entrenar\") | Notificar al entrenador inmediatamente. Motivo: `Cliente reportó dificultad grave: '{{dificultades_reportadas}}'. Requiere contacto inmediato del entrenador para ajuste de plan y posible derivación médica.` Plazo: **2 horas**. |\n| C3.2 | El cliente reporta la misma dificultad en **2 check-ins consecutivos** | Notificar al entrenador. Motivo: `Cliente reporta la misma dificultad por segundo check-in consecutivo: '{{dificultades_reportadas}}'. Requiere intervención para resolver la barrera.` Plazo: **48 horas**. |\n\n### C4 — Meta lograda\n\n| # | Condición | Acción |\n|---|---|---|\n| C4.1 | El cliente indica que alcanzó su meta | Notificar al entrenador. Motivo: `Cliente indica que alcanzó su meta: '{{meta_descripcion}}'. Requiere evaluación final y establecimiento de nueva meta.` Plazo: **48 horas**. |\n| C4.2 | `progreso_actual_porcentaje` ≥ 100 | Notificar al entrenador. Motivo: `Progreso alcanzado: {{progreso_actual_porcentaje}}% del objetivo cumplido. Cliente logró su meta de '{{meta_descripcion}}'.` Plazo: **48 horas**. |\n\n### C5 — Fallo técnico\n\n| # | Condición | Acción |\n|---|---|---|\n| C5.1 | El envío del check-in falla **2 veces consecutivas** (intento original + 1 reintento tras 30 segundos) | Notificar al entrenador. Motivo: `Error de envío por canal {{canal_preferido}} tras 2 intentos fallidos al enviar check-in a {{nombre_cliente}} (cliente {{cliente_id}}).` Plazo: **4 horas**. |\n| C5.2 | El `canal_preferido` no es uno de los 4 valores válidos (`whatsapp`, `sms`, `email`, `app`) | Notificar al entrenador. Motivo: `Canal preferido '{{canal_preferido}}' no reconocido para el cliente {{cliente_id}}.` Plazo: **4 horas**. |\n| C5.3 | El destino del envío está vacío o es inválido | Notificar al entrenador. Motivo: `Destino de envío inválido o vacío para canal {{canal_preferido}}.` Plazo: **4 horas**. |\n\n### C6 — Restricciones de configuración\n\n| # | Condición | Acción |\n|---|---|---|\n| C6.1 | El `entrenador_nombre` no está configurado o está vacío | Notificar al coordinador. Motivo: `Entrenador responsable no configurado para el cliente {{cliente_id}}. No se puede generar notificación de progreso.` Plazo: **4 horas**. |\n| C6.2 | La `meta_fecha` ya pasó y el progreso no se ha alcanzado | Notificar al entrenador + gerente. Motivo: `Fecha de meta alcanzada ({{fecha_meta}}) sin que se haya logrado el objetivo. Progreso actual: {{progreso_actual_porcentaje}}%. Requiere revisión y posible redefinición de meta.` Plazo: **24 horas**. |\n| C6.3 | El `zona_horaria` no está configurada o no es una zona horaria IANA válida | **No notifica.** Usar `America/Mexico_City` como valor por defecto. Registrar advertencia. |\n\n---\n\n## Resumen de tiempos de notificación\n\n| Condición | Tiempo antes de notificar | Notifica a gerente |\n|---|---|---|\n| Progreso por debajo (C1.1) | 0 segundos (inmediato) | No |\n| Estancamiento (C1.2) | 0 segundos (inmediato) | Sí |\n| Progreso muy por debajo (C1.3) | 0 segundos (inmediato) | Sí |\n| Satisfacción baja (C2.1) | 0 segundos (inmediato) | No |\n| Satisfacción baja sostenida (C2.2) | 0 segundos (inmediato) | Sí |\n| Dificultad grave (C3.1) | 0 segundos (inmediato) | No |\n| Dificultad repetida (C3.2) | 0 segundos (inmediato) | No |\n| Meta lograda (C4.1, C4.2) | 0 segundos (inmediato) | No |\n| Fallo envío (C5.1) | 0 segundos tras 1 reintento fallido | No |\n| Canal no reconocido (C5.2) | 0 segundos (inmediato) | No |\n| Destino inválido (C5.3) | 0 segundos (inmediato) | No |\n| Sin entrenador configurado (C6.1) | 0 segundos (inmediato) | No |\n| Meta vencida sin logro (C6.2) | 0 segundos (inmediato) | Sí |\n| Zona horaria inválida (C6.3) | N/A (usa default) | N/A |\n\n---\n\n## Plazos de respuesta del equipo interno\n\n| Tipo de notificación | Plazo de respuesta esperado |\n|---|---|\n| Dificultad grave del cliente (C3.1) | 2 horas |\n| Estancamiento (C1.2) | 24 horas |\n| Progreso muy por debajo (C1.3) | 24 horas |\n| Meta vencida sin logro (C6.2) | 24 horas |\n| Progreso por debajo (C1.1) | 48 horas |\n| Satisfacción baja (C2.1) | 48 horas |\n| Dificultad repetida (C3.2) | 48 horas |\n| Meta lograda (C4.1, C4.2) | 48 horas |\n| Satisfacción baja sostenida (C2.2) | 24 horas |\n| Fallo de envío (C5.1–C5.3) | 4 horas |\n| Sin entrenador configurado (C6.1) | 4 horas |\n\n---\n\n## Acciones sugeridas por estado de progreso\n\n| Estado de progreso | Acción sugerida para el entrenador |\n|---|---|\n| `adelantado` | Felicitar al cliente. Considerar ajustar la meta a un nivel más ambicioso si es apropiado. |\n| `en_curso` | Mantener el plan actual. Reforzar la importancia de la consistencia. |\n| `por_debajo` | Revisar disponibilidad horaria del cliente. Ajustar plan de entrenamiento. Considerar sesiones adicionales o cambio de enfoque. Evaluar si la meta es realista. |\n| `estancado` | Revisión completa del plan de entrenamiento y nutrición. Considerar cambiar de estrategia. Evaluar posibles barreras externas (lesión, estrés, alimentación). |\n\n---\n\n## Textos base de notificación\n\nLos siguientes textos se usan literalmente en las notificaciones. No se deben modificar ni parafrasear.\n\n| Condición | Texto del campo `detalles` |\n|---|---|\n| C1.1 | `\"Progreso {{diferencia_progreso}} puntos por debajo de lo esperado. Solo alcanzó {{progreso_actual_porcentaje}}% del objetivo con {{progreso_esperado_porcentaje}}% del tiempo transcurrido.\"` |\n| C1.2 | `\"Sin progreso visible después de {{semanas_transcurridas}} semanas. Progreso actual: {{progreso_actual_porcentaje}}%. Revisión urgente del plan de entrenamiento y/o nutrición.\"` |\n| C1.3 | `\"Progreso extremadamente por debajo: {{progreso_actual_porcentaje}}% vs {{progreso_esperado_porcentaje}}% esperado (diferencia: {{diferencia_progreso}} puntos). Requiere intervención inmediata.\"` |\n| C2.1 | `\"Cliente reporta baja satisfacción con su progreso ({{satisfaccion_progreso}}/5). Requiere contacto del entrenador para motivar y ajustar expectativas.\"` |\n| C2.2 | `\"Cliente reporta baja satisfacción (≤2/5) por 3 check-ins consecutivos. Riesgo de deserción por falta de resultados percibidos.\"` |\n| C3.1 | `\"Cliente reportó dificultad grave: '{{dificultades_reportadas}}'. Requiere contacto inmediato del entrenador para ajuste de plan y posible derivación médica.\"` |\n| C3.2 | `\"Cliente reporta la misma dificultad por segundo check-in consecutivo: '{{dificultades_reportadas}}'. Requiere intervención para resolver la barrera.\"` |\n| C4.1 | `\"Cliente indica que alcanzó su meta: '{{meta_descripcion}}'. Requiere evaluación final y establecimiento de nueva meta.\"` |\n| C4.2 | `\"Progreso alcanzado: {{progreso_actual_porcentaje}}% del objetivo cumplido. Cliente logró su meta de '{{meta_descripcion}}'.\"` |\n| C5.1 | `\"Error de envío por canal {{canal_preferido}} tras 2 intentos fallidos al enviar check-in a {{nombre_cliente}} (cliente {{cliente_id}}).\"` |\n| C5.2 | `\"Canal preferido '{{canal_preferido}}' no reconocido para el cliente {{cliente_id}}.\"` |\n| C5.3 | `\"Destino de envío inválido o vacío para canal {{canal_preferido}}.\"` |\n| C6.1 | `\"Entrenador responsable no configurado para el cliente {{cliente_id}}. No se puede generar notificación de progreso.\"` |\n| C6.2 | `\"Fecha de meta alcanzada ({{fecha_meta}}) sin que se haya logrado el objetivo. Progreso actual: {{progreso_actual_porcentaje}}%. Requiere revisión y posible redefinición de meta.\"` |\n\n---\n\n## Ejemplo completo de activación\n\n**Condición activada:** C3.1 (dificultad grave reportada)\n\n**Contexto:** El cliente Roberto Ruiz Méndez reportó en su check-in semanal: \"Me lastimé la espalda entrenando ayer, no puedo hacer ejercicios de espalda ni piernas\".\n\n**Flujo del agente:**\n1. Recibe respuesta del cliente con las 4 respuestas.\n2. Detecta palabra clave de dificultad grave: \"lastimé\".\n3. Registra como `dificultad_grave`.\n4. Notifica al entrenador inmediatamente.\n5. Genera registro de escalamiento.\n\n**Respuesta al cliente:**\n\n> ¡Gracias por tus respuestas, Roberto Ruiz! 💪 Tus datos han sido registrados.\n>\n> Tu entrenador **Coach Martínez** está revisando tu progreso y se pondrá en contacto contigo pronto si es necesario.\n>\n> ¡Sigue así! Cada paso cuenta. 🏋️\n\n**Notificación al entrenador:**\n\n> ⚠️ DIFICULTAD GRAVE REPORTADA — FitZone Premium\n> ==========================================\n>\n> **Cliente:** Roberto Ruiz Méndez\n> **Meta:** Ganar 3 kg de masa muscular en 3 meses\n> **Progreso actual:** 30% (vs 65% esperado)\n>\n> 📋 **Dificultad reportada:** \"Me lastimé la espalda entrenando ayer, no puedo hacer ejercicios de espalda ni piernas\"\n>\n> ⏱️ **Plazo de contacto:** 2 horas\n> 🔗 Check-in: GCI-20260824-150000-003\n\n**Registro de escalamiento:**\n\n```json\n{\n  \"registro_id\": \"GCIREG-20260824-160010-003\",\n  \"checkin_id\": \"GCI-20260824-150000-003\",\n  \"evento\": \"escalamiento\",\n  \"fecha_hora_ejecucion\": \"2026-08-24T16:00:10\",\n  \"agente\": \"goal-check-in-assistant\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"nombre_cliente\": \"Roberto Ruiz Méndez\",\n  \"telefono_cliente\": \"+52 55 4567 8901\",\n  \"email_cliente\": \"roberto.ruiz@correo.com\",\n  \"canal_preferido\": \"whatsapp\",\n  \"meta_id\": \"META-20260701-003\",\n  \"meta_descripcion\": \"Ganar 3 kg de masa muscular en 3 meses\",\n  \"resultado\": \"escalamiento\",\n  \"detalles\": \"Motivo: Cliente reportó dificultad grave: 'Me lastimé la espalda entrenando ayer, no puedo hacer ejercicios de espalda ni piernas'. Requiere contacto inmediato del entrenador para ajuste de plan y posible derivación médica.\",\n  \"responsable_escalamiento\": \"Coach Martínez\",\n  \"canal_escalamiento\": \"whatsapp_interno\",\n  \"plazo_respuesta_horas\": 2,\n  \"datos_origen\": {\n    \"checkin_id\": \"GCI-20260824-150000-003\",\n    \"cliente_id\": \"CLI-20260701-301\",\n    \"nombre_cliente\": \"Roberto Ruiz Méndez\",\n    \"meta_descripcion\": \"Ganar 3 kg de masa muscular en 3 meses\",\n    \"progreso_actual_porcentaje\": 30,\n    \"progreso_esperado_porcentaje\": 65\n  }\n}\n```\n"
              },
              {
                "id": "goal-check-in-assistant/variables-entrada.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "variables-entrada.md",
                "contenido": "# Variables de entrada — Goal Check-in Assistant\n\nEste archivo define la lista fija y cerrada de campos de datos que el agente necesita recibir para operar. No se admiten campos adicionales a los listados aquí.\n\n---\n\n## Campos del evento disparador\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 1 | `evento_disparador_id` | `string` | Obligatorio | Identificador único del evento disparador. Formato: `GCI-YYYYMMDD-HHMMSS-XXX`. | `\"GCI-20260824-100000-001\"` |\n| 2 | `cliente_id` | `string` | Obligatorio | Identificador del cliente. Formato: `CLI-YYYYMMDD-XXX`. | `\"CLI-20260115-101\"` |\n| 3 | `contacto_id` | `string` | Obligatorio | Identificador del contacto asociado. Formato: `CON-YYYYMMDD-XXX`. | `\"CON-20260115-101\"` |\n| 4 | `fecha_actual` | `string` (ISO 8601: `YYYY-MM-DD`) | Obligatorio | Fecha actual del sistema para el cálculo de intervalos. | `\"2026-08-24\"` |\n\n---\n\n## Datos del cliente\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 5 | `nombre_cliente` | `string` | Obligatorio | Nombre completo del cliente. | `\"Ana García López\"` |\n| 6 | `telefono_cliente` | `string` | Obligatorio | Número telefónico del cliente. Entre 8 y 15 dígitos. | `\"+52 55 9876 5432\"` |\n| 7 | `email_cliente` | `string` | Opcional (requerido si `canal_preferido` = `email`) | Correo electrónico del cliente. | `\"ana.garcia@correo.com\"` |\n| 8 | `canal_preferido` | `string` (enum) | Obligatorio | Canal preferido para enviar check-ins. Valores: `whatsapp`, `sms`, `email`, `app`. | `\"whatsapp\"` |\n| 9 | `estado_cliente` | `string` (enum) | Obligatorio | Estado del cliente en el sistema. Solo se procesan clientes con estado `activo`. Valores: `activo`, `suspendido`, `baja`. | `\"activo\"` |\n| 10 | `fecha_inicio_membresia` | `string` (ISO 8601: `YYYY-MM-DD`) | Obligatorio | Fecha de inicio de la membresía del cliente. | `\"2026-01-15\"` |\n\n---\n\n## Datos de historial de check-ins\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 11 | `fecha_ultimo_checkin` | `string` (ISO 8601: `YYYY-MM-DD`) o `null` | Obligatorio | Fecha del último check-in registrado. `null` si no hay check-ins previos. | `\"2026-08-17\"` |\n| 12 | `total_checkins_realizados` | `integer` | Obligatorio | Número total de check-ins realizados por el cliente. | `3` |\n| 13 | `intervalo_checkin_dias` | `integer` | Obligatorio | Días de intervalo entre check-ins. Valores: `7`, `14`, `30`. | `7` |\n| 14 | `historial_checkins` | `array` de objects | Opcional | Lista de check-ins anteriores con sus respuestas. (ver estructura) | (ver abajo) |\n\n### Estructura de cada check-in en `historial_checkins`\n\n| Campo | Tipo | Descripción |\n|---|---|---|\n| `checkin_id` | `string` | ID del check-in anterior. |\n| `fecha_checkin` | `string` (ISO 8601) | Fecha del check-in. |\n| `asistencias_semana` | `integer` | Asistencias reportadas. |\n| `peso_actual_kg` | `number` | Peso reportado. |\n| `satisfaccion_progreso` | `integer` (1–5) | Satisfacción reportada. |\n| `dificultades_reportadas` | `string` | Dificultades reportadas. |\n| `estado_progreso` | `string` | Estado del progreso calculado. |\n\n---\n\n## Datos de la meta\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 15 | `meta_id` | `string` | Obligatorio | Identificador único de la meta. Formato: `META-YYYYMMDD-XXX`. | `\"META-20260301-001\"` |\n| 16 | `meta_tipo` | `string` (enum) | Obligatorio | Tipo de meta. Valores: `perdida_peso`, `ganancia_muscular`, `asistencia`, `rendimiento`, `medidas`. | `\"perdida_peso\"` |\n| 17 | `meta_descripcion` | `string` | Obligatorio | Descripción legible de la meta. | `\"Perder 10 kg en 6 meses\"` |\n| 18 | `meta_valor_objetivo` | `number` | Obligatorio | Valor numérico objetivo de la meta. | `65.0` |\n| 19 | `meta_unidad` | `string` | Obligatorio | Unidad de medida de la meta. | `\"kg\"` |\n| 20 | `fecha_inicio_meta` | `string` (ISO 8601: `YYYY-MM-DD`) | Obligatorio | Fecha de inicio de la meta. | `\"2026-03-01\"` |\n| 21 | `fecha_meta` | `string` (ISO 8601: `YYYY-MM-DD`) | Obligatorio | Fecha límite para alcanzar la meta. | `\"2026-09-01\"` |\n| 22 | `valor_inicial` | `number` | Obligatorio | Valor inicial registrado al establecer la meta. | `75.0` |\n| 23 | `peso_actual` | `number` | Opcional (requerido si `meta_tipo` es `perdida_peso` o `ganancia_muscular`) | Peso actual más reciente registrado en el sistema. | `70.5` |\n| 24 | `meta_estado` | `string` (enum) | Obligatorio | Estado actual de la meta. Valores: `activa`, `completada`, `cancelada`, `pausada`. | `\"activa\"` |\n\n---\n\n## Datos del entrenador responsable\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 25 | `entrenador_id` | `string` | Obligatorio | Identificador del entrenador responsable. | `\"TRN-001\"` |\n| 26 | `entrenador_nombre` | `string` | Obligatorio | Nombre del entrenador responsable del cliente. | `\"Coach Martínez\"` |\n| 27 | `entrenador_telefono` | `string` | Opcional | Teléfono del entrenador. | `\"+52 55 2222 3333\"` |\n| 28 | `entrenador_email` | `string` | Opcional | Email del entrenador. | `\"martinez@fitzonepremium.com\"` |\n\n---\n\n## Datos del gimnasio (inyectados desde configuración)\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 29 | `gimnasio_id` | `string` | Obligatorio | Identificador único del gimnasio. | `\"GYM-001\"` |\n| 30 | `gimnasio_nombre` | `string` | Obligatorio | Nombre comercial del gimnasio. | `\"FitZone Premium\"` |\n| 31 | `gimnasio_telefono` | `string` | Obligatorio | Teléfono principal del gimnasio. | `\"+52 55 4321 8765\"` |\n\n---\n\n## Datos del destinatario de notificación\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 32 | `responsable_nombre` | `string` | Obligatorio | Nombre del responsable que recibe notificaciones. | `\"Coach Martínez\"` |\n| 33 | `responsable_telefono` | `string` | Opcional | Teléfono del responsable. | `\"+52 55 2222 3333\"` |\n| 34 | `responsable_email` | `string` | Opcional | Email del responsable. | `\"martinez@fitzonepremium.com\"` |\n| 35 | `gerente_nombre` | `string` | Opcional (requerido si progreso es `estancado`) | Nombre del gerente para notificaciones de estancamiento. | `\"Luis Herrera\"` |\n| 36 | `gerente_telefono` | `string` | Opcional | Teléfono del gerente. | `\"+52 55 3333 4444\"` |\n| 37 | `canal_notificacion_equipo` | `string` (enum) | Obligatorio | Canal para notificar al equipo interno. Valores: `whatsapp_interno`, `email_interno`. | `\"whatsapp_interno\"` |\n\n---\n\n## Configuración de tiempos (inyectada desde configuración)\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 38 | `plazo_respuesta_checkin_horas` | `integer` | Obligatorio | Horas de espera para respuesta del cliente después del check-in. Valor por defecto: `48`. | `48` |\n| 39 | `plazo_seguimiento_pordebajo_horas` | `integer` | Obligatorio | Horas dentro de las cuales el entrenador debe dar seguimiento si progreso está por debajo. Valor por defecto: `48`. | `48` |\n| 40 | `plazo_seguimiento_estancado_horas` | `integer` | Obligatorio | Horas dentro de las cuales se debe dar seguimiento si progreso está estancado. Valor por defecto: `24`. | `24` |\n| 41 | `hora_preferida_envio` | `string` (formato 24h: `HH:MM`) | Opcional | Hora preferida para enviar check-ins si caen fuera de horario. Valor por defecto: `\"10:00\"`. | `\"10:00\"` |\n| 42 | `envio_fueradehorario_permitido` | `boolean` | Obligatorio | `true` si se permite enviar check-ins fuera del horario de atención. `false` si se ajusta al horario. | `false` |\n\n---\n\n## Campos calculados por el agente\n\nEstos campos **no se reciben de entrada**; el agente los genera internamente.\n\n| # | Nombre del campo | Tipo de dato | Generado en | Descripción |\n|---|---|---|---|---|\n| 43 | `checkin_id` | `string` | Paso 1 | ID único del check-in. Formato: `GCI-YYYYMMDD-HHMMSS-XXX`. |\n| 44 | `fecha_hora_recepcion` | `string` (ISO 8601) | Paso 1 | Marca de tiempo de recepción del evento. |\n| 45 | `dias_desde_ultimo_checkin` | `integer` | Paso 1 | Días transcurridos desde el último check-in. |\n| 46 | `intervalo_cumplido` | `boolean` | Paso 2 | `true` si se cumplió el intervalo. |\n| 47 | `semanas_transcurridas` | `integer` | Paso 2 | Semanas transcurridas desde el inicio de la meta. |\n| 48 | `semanas_totales_meta` | `integer` | Paso 2 | Total de semanas de la meta. |\n| 49 | `semanas_restantes` | `integer` | Paso 2 | Semanas restantes hasta la fecha de la meta. |\n| 50 | `progreso_actual_porcentaje` | `integer` | Paso 4 | Porcentaje de progreso actual. |\n| 51 | `progreso_esperado_porcentaje` | `integer` | Paso 4 | Porcentaje de progreso esperado. |\n| 52 | `diferencia_progreso` | `integer` | Paso 4 | Diferencia entre progreso actual y esperado. |\n| 53 | `estado_progreso` | `string` (enum) | Paso 4 | Estado del progreso. |\n| 54 | `peso_necesario_para_meta` | `number` o `null` | Paso 4 | Peso necesario para alcanzar la meta en el tiempo restante. |\n| 55 | `notificar_humano` | `boolean` | Paso 4 | `true` si se requiere notificación al entrenador. |\n| 56 | `motivo_notificacion` | `string` o `null` | Paso 4 | Motivo de la notificación. |\n| 57 | `fecha_hora_envio` | `string` (ISO 8601) o `null` | Paso 2 | Marca de tiempo de envío del check-in. |\n| 58 | `estado_checkin` | `string` (enum) | Paso 2–3 | Estado del check-in. |\n| 59 | `asistencias_semana` | `integer` o `null` | Paso 3 | Respuesta del cliente: asistencias. |\n| 60 | `peso_actual_kg` | `number` o `null` | Paso 3 | Respuesta del cliente: peso. |\n| 61 | `satisfaccion_progreso` | `integer` o `null` | Paso 3 | Respuesta del cliente: satisfacción (1–5). |\n| 62 | `dificultades_reportadas` | `string` o `null` | Paso 3 | Respuesta del cliente: dificultades. |\n| 63 | `fecha_hora_respuesta` | `string` (ISO 8601) o `null` | Paso 3 | Marca de tiempo de la respuesta. |\n| 64 | `fecha_hora_notificacion` | `string` (ISO 8601) o `null` | Paso 5 | Marca de tiempo de notificación al entrenador. |\n| 65 | `fecha_hora_cierre` | `string` (ISO 8601) | Paso 5 | Marca de tiempo de cierre. |\n| 66 | `resultado` | `string` (enum) | Paso 5 | Resultado final. |\n| 67 | `duracion_proceso_segundos` | `integer` | Paso 5 | Duración total del proceso en segundos. |\n\n---\n\n## Validaciones de entrada\n\n| Campo | Regla | Acción si falla |\n|---|---|---|\n| `evento_disparador_id` | No vacío, formato válido | Terminar flujo sin ejecutar |\n| `cliente_id` | No vacío, formato `CLI-XXXXXXXX-XXX` | Terminar flujo sin ejecutar |\n| `contacto_id` | Debe seguir formato `CON-XXXXXXXX-XXX` | Terminar flujo sin ejecutar |\n| `fecha_actual` | Formato `YYYY-MM-DD` | Usar fecha del sistema |\n| `nombre_cliente` | No vacío, mínimo 2 caracteres | Usar `\"Cliente\"` como valor por defecto |\n| `telefono_cliente` | Entre 8 y 15 dígitos si `canal_preferido` es `whatsapp` o `sms` | Escalar a humano (C3.1) |\n| `email_cliente` | Si `canal_preferido` es `email`, debe ser formato válido | Escalar a humano (C3.1) |\n| `canal_preferido` | Debe ser uno de los 4 valores válidos | Usar `\"whatsapp\"` como valor por defecto |\n| `estado_cliente` | Debe ser `activo` | No procesar check-in si no es activo |\n| `fecha_inicio_membresia` | Formato `YYYY-MM-DD` | Terminar flujo sin ejecutar |\n| `fecha_ultimo_checkin` | Formato `YYYY-MM-DD` o `null` | Usar `null` como valor por defecto |\n| `total_checkins_realizados` | Número entero ≥ 0 | Usar `0` como valor por defecto |\n| `intervalo_checkin_dias` | Debe ser `7`, `14` o `30` | Usar `7` como valor por defecto |\n| `meta_id` | No vacío, formato `META-XXXXXXXX-XXX` | Terminar flujo sin ejecutar |\n| `meta_tipo` | Debe ser uno de los 5 valores válidos | Terminar flujo sin ejecutar |\n| `meta_descripcion` | No vacío | Terminar flujo sin ejecutar |\n| `meta_valor_objetivo` | Debe ser número | Terminar flujo sin ejecutar |\n| `meta_unidad` | No vacío | Usar `\"unidad\"` como valor por defecto |\n| `fecha_inicio_meta` | Formato `YYYY-MM-DD` | Terminar flujo sin ejecutar |\n| `fecha_meta` | Formato `YYYY-MM-DD`, posterior a `fecha_inicio_meta` | Terminar flujo sin ejecutar |\n| `valor_inicial` | Debe ser número | Terminar flujo sin ejecutar |\n| `peso_actual` | Si `meta_tipo` es `perdida_peso` o `ganancia_muscular`, debe ser número | Usar `valor_inicial` como valor por defecto |\n| `meta_estado` | Debe ser `activa`, `completada`, `cancelada` o `pausada` | Si no es `activa`, no procesar check-in |\n| `entrenador_id` | No vacío | Terminar flujo sin ejecutar |\n| `entrenador_nombre` | No vacío | Usar `\"Entrenador\"` como valor por defecto |\n| `gimnasio_id` | No vacío | Terminar flujo sin ejecutar |\n| `gimnasio_nombre` | No vacío | Terminar flujo sin ejecutar |\n| `responsable_nombre` | No vacío | Usar `entrenador_nombre` como valor por defecto |\n| `canal_notificacion_equipo` | Debe ser uno de los 2 valores válidos | Usar `\"whatsapp_interno\"` como valor por defecto |\n"
              }
            ]
          }
        ]
      },
      {
        "id": "nutrition-plan-faq",
        "carpeta": "nutrition-plan-faq-agent",
        "nombre": "Nutrition Plan FAQ Agent",
        "icono": "🥗",
        "descripcion": "Responder preguntas sobre planes de nutrición y alimentación",
        "submodulos": [],
        "categoria": "general",
        "estructura": [
          {
            "id": "nutrition-plan-faq-agent",
            "etiqueta": "",
            "es_plantilla": true,
            "nombre": "nutrition-plan-faq-agent",
            "subcarpetas": [],
            "notas": [
              {
                "id": "nutrition-plan-faq-agent/flujo.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "flujo.md",
                "contenido": "# Flujo de ejecución — Nutrition Plan FAQ Agent\n\nEste archivo documenta el flujo fijo de ejecución del agente. Cada consulta de nutrición sigue estos 5 pasos **en orden**, sin pasos condicionales ni bifurcaciones.\n\n---\n\n## Paso 1 — Recepción de la pregunta del usuario en texto libre\n\nEl agente recibe la pregunta del usuario sobre planes de nutrición, alimentación o servicios de nutrición a través de cualquier canal configurado.\n\n**Acción del agente:**\n1. Recibir el evento disparador `pregunta_nutricion`.\n2. Extraer los campos de entrada definidos en `variables-entrada.md`.\n3. Registrar la marca de tiempo de recepción en `fecha_hora_recepcion`.\n4. Generar un `consulta_id` único para el caso.\n5. Verificar que la pregunta no esté vacía y tenga al menos 3 caracteres.\n6. Clasificar la pregunta en una de las categorías definidas en `prompt-sistema.md`.\n7. Extraer los datos del contacto: `nombre_contacto`, `telefono_contacto`, `email_contacto`, `canal_origen`.\n8. Obtener los datos del gimnasio: `gimnasio_nombre`, `gimnasio_telefono`, `gimnasio_direccion`.\n\n**Ejemplo de entrada:**\n\n```json\n{\n  \"evento\": \"pregunta_nutricion\",\n  \"canal_origen\": \"whatsapp\",\n  \"numero_remitente\": \"+52 55 9876 5432\",\n  \"mensaje\": \"Hola, ¿cuánto cuesta una consulta de nutrición? Soy vegano y quiero bajar de peso\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"gimnasio_telefono\": \"+52 55 4321 8765\"\n}\n```\n\n---\n\n## Paso 2 — Comparación de la pregunta contra la base de conocimiento del negocio\n\nEl agente compara la pregunta del usuario contra la base de conocimiento predefinida para determinar si la pregunta está cubierta.\n\n**Acción del agente:**\n1. Analizar el `mensaje` del usuario para extraer:\n   - `tema_principal`: tema principal de la pregunta (ej. \"precios\", \"servicios\", \"horarios\").\n   - `subtema`: subtema específico si existe (ej. \"consulta inicial\", \"plan vegano\").\n   - `keywords`: palabras clave relevantes (ej. \"vegano\", \"bajar de peso\", \"gluten\", \"diabético\").\n2. Buscar en la base de conocimiento (`base_conocimiento`) las entradas que coincidan con el tema y subtema.\n3. Clasificar la cobertura de la pregunta:\n   - `cubierta_total`: la pregunta tiene una respuesta completa en la base de conocimiento.\n   - `cubierta_parcial`: la base tiene información relacionada pero no una respuesta directa.\n   - `no_cubierta`: la pregunta no tiene ninguna respuesta en la base de conocimiento.\n4. Si es `cubierta_total` o `cubierta_parcial`, seleccionar la respuesta correspondiente de la base de conocimiento.\n5. Si es `no_cubierta`, marcar para escalamiento (paso 4).\n6. Registrar `categoria_pregunta`, `tema_principal`, `subtema`, `keywords` y `cobertura`.\n\n**Ejemplo de comparación:**\n\n```json\n{\n  \"consulta_id\": \"NFAQ-20260824-103000-001\",\n  \"mensaje_original\": \"Hola, ¿cuánto cuesta una consulta de nutrición? Soy vegano y quiero bajar de peso\",\n  \"categoria_pregunta\": \"PRECIOS_NUTRICION\",\n  \"tema_principal\": \"precios\",\n  \"subtema\": \"consulta_inicial\",\n  \"keywords\": [\"consulta\", \"nutrición\", \"vegano\", \"bajar de peso\"],\n  \"cobertura\": \"cubierta_total\",\n  \"respuesta_base_conocimiento\": \"PRECIO_CONSULTA_INICIAL\"\n}\n```\n\n---\n\n## Paso 3 — Redacción de una respuesta clara y directa usando únicamente datos de la base de conocimiento\n\nEl agente redacta la respuesta al usuario utilizando exclusivamente la información de la base de conocimiento.\n\n**Acción del agente:**\n1. Si la `cobertura` es `cubierta_total`:\n   a. Obtener la plantilla de respuesta de la base de conocimiento.\n   b. Insertar los datos relevantes del usuario y del gimnasio.\n   c. Generar el `texto_respuesta`.\n2. Si la `cobertura` es `cubierta_parcial`:\n   a. Obtener la información parcial disponible.\n   b. Redactar una respuesta con lo que se sabe, indicando que para detalles específicos se requiere asesoría humana.\n   c. Generar el `texto_respuesta`.\n3. Validar que la respuesta:\n   - No exceda 500 caracteres por párrafo.\n   - Incluya la información solicitada.\n   - Ofrezca una acción siguiente (agendar consulta, llamar, etc.).\n   - No incluya opiniones personales ni consejos médicos.\n4. Generar el `texto_respuesta` con la plantilla de `formato-salida.md` → sección 1.\n5. Registrar `estado_respuesta` como `generada`.\n\n**Tabla de respuestas de la base de conocimiento por categoría:**\n\n| Categoría | ID de respuesta | Contenido |\n|---|---|---|\n| `PLANES_NUTRICION` | `PLANES_DISPONIBLES` | Lista de planes de nutrición con descripción y precio |\n| `PLANES_NUTRICION` | `PLAN_CONSULTA_INICIAL` | Detalles de la consulta inicial de nutrición |\n| `PLANES_NUTRICION` | `PLAN_SEGUIMIENTO_MENSUAL` | Detalles del plan de seguimiento mensual |\n| `PLANES_NUTRICION` | `PLAN_ELITE` | Detalles del plan elite (entrenamiento + nutrición) |\n| `SERVICIOS_NUTRICION` | `SERVICIO_CONSULTA` | Información sobre el servicio de consulta |\n| `SERVICIOS_NUTRICION` | `SERVICIO_PLAN_PERSONALIZADO` | Información sobre planes personalizados |\n| `SERVICIOS_NUTRICION` | `SERVICIO_GRUPO_WHATSAPP` | Información sobre el grupo de WhatsApp |\n| `HORARIOS_NUTRICION` | `HORARIOS_DISPONIBLES` | Horarios del nutriólogo |\n| `HORARIOS_NUTRICION` | `HORARIOS_FIN_SEMANA` | Horarios de sábado y domingo |\n| `PRECIOS_NUTRICION` | `PRECIO_CONSULTA_INICIAL` | Precio de la consulta inicial |\n| `PRECIOS_NUTRICION` | `PRECIO_SEGUIMIENTO` | Precio del seguimiento mensual |\n| `PRECIOS_NUTRICION` | `PRECIO_ELITE` | Precio del plan elite |\n| `PRECIOS_NUTRICION` | `METODOS_PAGO` | Métodos de pago aceptados |\n| `PRECIOS_NUTRICION` | `DESCUENTOS` | Descuentos disponibles |\n| `BENEFICIOS_NUTRICION` | `BENEFICIOS_GENERALES` | Beneficios generales del servicio |\n| `BENEFICIOS_NUTRICION` | `RESULTADOS_ESPERADOS` | Resultados típicos reportados |\n| `RESTRICCIONES_NUTRICION` | `RESTRICCIONES_VEGANO` | Opciones para veganos |\n| `RESTRICCIONES_NUTRICION` | `RESTRICCIONES_GLUTEN` | Opciones sin gluten |\n| `RESTRICCIONES_NUTRICION` | `RESTRICCIONES_DIABETICOS` | Opciones para diabéticos |\n| `RESTRICCIONES_NUTRICION` | `RESTRICCIONES_ALERGIAS` | Manejo de alergias alimenticias |\n| `ENTREGA_NUTRICION` | `ENTREGA_FORMATO` | Formato de entrega del plan |\n| `ENTREGA_NUTRICION` | `ENTREGA_FRECUENCIA` | Frecuencia de ajustes |\n| `ENTREGA_NUTRICION` | `ENTREGA_CAMBIO_PLAN` | Procedimiento para cambiar de plan |\n\n---\n\n## Paso 4 — Si la pregunta no está cubierta, respuesta indicando que un humano dará seguimiento y creación del caso\n\nEl agente genera una respuesta indicando que la consulta requiere atención humana y crea un caso de escalamiento.\n\n**Acción del agente:**\n1. Si la `cobertura` es `no_cubierta`:\n   a. Generar el mensaje de escalamiento usando la plantilla de `formato-salida.md` → sección 2.\n   b. Insertar el `tema_consulta` (resumen del tema de la pregunta) en el mensaje.\n   c. Generar el `consulta_id` del caso de escalamiento.\n   d. Registrar el `motivo_escalamiento`.\n2. Si la `cobertura` es `cubierta_parcial`:\n   a. Generar la respuesta con la información parcial disponible.\n   b. Al final de la respuesta, agregar una nota indicando que para detalles específicos puede contactar al equipo.\n3. Registrar `estado_consulta` como `escalada` o `respondida_parcial`.\n\n---\n\n## Paso 5 — Registro de la pregunta y respuesta en el historial de conversación\n\nEl agente registra toda la interacción en el sistema para auditoría y seguimiento.\n\n**Acción del agente:**\n1. Generar el registro completo de la consulta con el formato de `formato-salida.md` → sección 4 (registro JSON).\n2. Registrar la marca de tiempo de cierre en `fecha_hora_cierre`.\n3. Calcular `duracion_proceso_segundos` = `fecha_hora_cierre` - `fecha_hora_recepcion`.\n4. Actualizar `estado_consulta` a `respondida`, `respondida_parcial` o `escalada`.\n5. Guardar el registro en el sistema de historial de contactos.\n6. Si el contacto tiene un perfil existente, asociar el `consulta_id` al historial del contacto.\n\n**Tabla de resultados posibles:**\n\n| Resultado | Descripción | Próxima acción del sistema |\n|---|---|---|\n| `respondida` | La pregunta fue respondida con información completa de la base de conocimiento | Cerrar caso. No requiere seguimiento humano. |\n| `respondida_parcial` | La pregunta fue respondida con información parcial | Registrar. Seguimiento humano en 24h si el contacto lo solicita. |\n| `escalada` | La pregunta no está cubierta, se creó caso para humano | Esperar resolución del humano. Plazo según categoría. |\n| `envio_fallido` | No se pudo entregar la respuesta al contacto | Registrar error. No hay escalamiento para fallos de envío de FAQ. |\n"
              },
              {
                "id": "nutrition-plan-faq-agent/formato-salida.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "formato-salida.md",
                "contenido": "# Formato de salida — Nutrition Plan FAQ Agent\n\nEste archivo define la estructura exacta de los mensajes y registros que el agente debe producir. No se permite desviarse de estas plantillas.\n\n---\n\n## 1. Respuesta a pregunta frecuente — Mensaje al contacto\n\nSe genera en el paso 3 del flujo. Se envía por el canal configurado (`canal_origen`).\n\n### Plantilla — Respuesta completa (whatsapp/sms)\n\n```\n¡Hola {{nombre_contacto}}! 🥗 Gracias por tu pregunta sobre nutrición.\n\n{{texto_respuesta}}\n\n{{texto_accion_sugerida}}\n\n— {{gimnasio_nombre}}\n```\n\n### Plantilla — Respuesta completa (email)\n\n**Asunto:** Tu consulta de nutrición — {{gimnasio_nombre}}\n\n```\nHola {{nombre_contacto}},\n\nGracias por contactar a {{gimnasio_nombre}} sobre nutrición.\n\n{{texto_respuesta}}\n\n{{texto_accion_sugerida}}\n\nSi tienes más preguntas, no dudes en escribirnos o llámanos al {{gimnasio_telefono}}.\n\n¡Te esperamos! 🥗\n— {{gimnasio_nombre}}\n```\n\n### Ejemplo renderizado (whatsapp — pregunta sobre precios)\n\n> ¡Hola Ana García López! 🥗 Gracias por tu pregunta sobre nutrición.\n>\n> En **FitZone Premium** ofrecemos los siguientes servicios de nutrición:\n>\n> 🍽️ **Consulta inicial** — $350 MXN (45 minutos)\n> • Evaluación de hábitos alimenticios\n> • Análisis de composición corporal\n> • Plan alimenticio básico personalizado\n>\n> 📋 **Plan de seguimiento mensual** — $800 MXN/mes\n> • Ajustes semanales del plan\n> • Seguimiento de progreso\n> • Acceso a grupo de WhatsApp con el nutriólogo\n>\n> 🏆 **Plan elite (entrenamiento + nutrición)** — $1,500 MXN/mes\n> • Todo lo anterior + plan de entrenamiento personalizado\n>\n> ¿Te gustaría agendar una consulta? Responde \"📅 Agendar consulta\".\n> — FitZone Premium\n\n### Ejemplo renderizado (whatsapp — pregunta sobre restricciones)\n\n> ¡Hola Ana García López! 🥗 Gracias por tu pregunta.\n>\n> Sí, en **FitZone Premium** tenemos opciones para diferentes necesidades:\n>\n> 🌱 **Veganos/vegetarianos:** Planes 100% plant-based con todas las proteínas y nutrientes necesarios.\n> 🌾 **Sin gluten:** Planes libres de gluten con alternativas seguras.\n> 🩺 **Diabéticos:** Planes adaptados con control de carbohidratos y glucemia.\n> ⚠️ **Alergias:** Evaluación personalizada para manejar cualquier alergia alimenticia.\n>\n> Para un plan personalizado, te recomendamos agendar una consulta inicial ($350 MXN, 45 minutos).\n>\n> ¿Te gustaría agendar? Responde \"📅 Agendar consulta\".\n> — FitZone Premium\n\n---\n\n## 2. Respuesta de escalamiento — Cuando la pregunta no está cubierta\n\nSe genera en el paso 4 del flujo. Se envía por el canal configurado (`canal_origen`).\n\n### Plantilla — Escalamiento (whatsapp/sms)\n\n```\n¡Hola {{nombre_contacto}}! 🥗 Gracias por tu pregunta.\n\nTu consulta sobre **{{tema_consulta}}** requiere una respuesta personalizada que no puedo generar con la información disponible.\n\nUn especialista en nutrición de **{{gimnasio_nombre}}** se pondrá en contacto contigo en las próximas **{{plazo_contacto_horas}} horas** para ayudarte.\n\nSi necesitas algo urgente, llámanos al **{{gimnasio_telefono}}**.\n— {{gimnasio_nombre}}\n```\n\n### Plantilla — Escalamiento (email)\n\n**Asunto:** Tu consulta de nutrición — {{gimnasio_nombre}}\n\n```\nHola {{nombre_contacto}},\n\nGracias por tu pregunta sobre nutrición en {{gimnasio_nombre}}.\n\nTu consulta sobre **{{tema_consulta}}** requiere una respuesta personalizada que no podemos generar con la información disponible en línea.\n\nUn especialista en nutrición se pondrá en contacto contigo en las próximas **{{plazo_contacto_horas}} horas** para ayudarte.\n\nSi necesitas algo urgente, llámanos al **{{gimnasio_telefono}}** o visítanos en **{{gimnasio_direccion}}**.\n\n¡Te esperamos! 🥗\n— {{gimnasio_nombre}}\n```\n\n### Ejemplo renderizado (whatsapp — escalamiento)\n\n> ¡Hola Ana García López! 🥗 Gracias por tu pregunta.\n>\n> Tu consulta sobre **plan personalizado para embarazadas** requiere una respuesta personalizada que no puedo generar con la información disponible.\n>\n> Un especialista en nutrición de **FitZone Premium** se pondrá en contacto contigo en las próximas **24 horas** para ayudarte.\n>\n> Si necesitas algo urgente, llámanos al **+52 55 4321 8765**.\n> — FitZone Premium\n\n---\n\n## 3. Notificación de escalamiento al equipo interno\n\n### Plantilla (whatsapp_interno)\n\n```\n📋 PREGUNTA ESCALADA — Nutrición — {{gimnasio_nombre}}\n==========================================\n\n**Contacto:** {{nombre_contacto}}\n**Teléfono:** {{telefono_contacto}}\n**Canal:** {{canal_origen}}\n\n❓ **Pregunta:** \"{{mensaje_pregunta}}\"\n🏷️ **Tema:** {{tema_consulta}}\n📋 **Categoría detectada:** {{categoria_pregunta}}\n\n🔗 Registro: {{consulta_id}}\n⏱️ Plazo de contacto: {{plazo_contacto_horas}} horas\n```\n\n### Plantilla (email_interno)\n\n**Asunto:** Pregunta escalada — {{nombre_contacto}} — Nutrición — {{gimnasio_nombre}}\n\n```\nPregunta de nutrición escalada a humano en {{gimnasio_nombre}}.\n\nContacto: {{nombre_contacto}}\nTeléfono: {{telefono_contacto}}\nCanal: {{canal_origen}}\n\nPregunta: \"{{mensaje_pregunta}}\"\nTema: {{tema_consulta}}\nCategoría detectada: {{categoria_pregunta}}\n\nRegistro: {{consulta_id}}\nPlazo de contacto: {{plazo_contacto_horas}} horas\n```\n\n### Ejemplo renderizado (whatsapp_interno)\n\n> 📋 PREGUNTA ESCALADA — Nutrición — FitZone Premium\n> ==========================================\n>\n> **Contacto:** Ana García López\n> **Teléfono:** +52 55 9876 5432\n> **Canal:** whatsapp\n>\n> ❓ **Pregunta:** \"¿Pueden hacer un plan de nutrición para embarazadas de 7 meses?\"\n> 🏷️ **Tema:** Plan personalizado para embarazadas\n> 📋 **Categoría detectada:** RESTRICCIONES_NUTRICION\n>\n> 🔗 Registro: NFAQ-20260824-103000-002\n> ⏱️ Plazo de contacto: 24 horas\n\n---\n\n## 4. Registro de consulta completa (JSON)\n\nSe genera al finalizar el paso 5 del flujo para cada consulta procesada.\n\n```json\n{\n  \"registro_id\": \"NFAQREG-YYYYMMDD-HHMMSS-XXX\",\n  \"consulta_id\": \"NFAQ-YYYYMMDD-HHMMSS-XXX\",\n  \"evento_id\": \"string\",\n  \"contacto_id\": \"CON-YYYYMMDD-XXX\",\n  \"gimnasio_id\": \"string\",\n  \"gimnasio_nombre\": \"string\",\n  \"nombre_contacto\": \"string\",\n  \"telefono_contacto\": \"string\",\n  \"email_contacto\": \"string | null\",\n  \"canal_origen\": \"whatsapp | sms | email | app | web\",\n  \"es_miembro_actual\": true | false,\n  \"mensaje_pregunta\": \"string\",\n  \"fecha_hora_recepcion\": \"YYYY-MM-DDTHH:MM:SS\",\n  \"categoria_pregunta\": \"PLANES_NUTRICION | SERVICIOS_NUTRICION | HORARIOS_NUTRICION | PRECIOS_NUTRICION | BENEFICIOS_NUTRICION | RESTRICCIONES_NUTRICION | ENTREGA_NUTRICION | OTRO_NUTRICION\",\n  \"tema_principal\": \"string\",\n  \"subtema\": \"string | null\",\n  \"keywords_detectadas\": [\"string\"],\n  \"cobertura\": \"cubierta_total | cubierta_parcial | no_cubierta\",\n  \"entrada_base_conocimiento\": \"string | null\",\n  \"texto_respuesta\": \"string\",\n  \"fecha_hora_envio\": \"YYYY-MM-DDTHH:MM:SS | null\",\n  \"estado_envio\": \"enviada | error_envio\",\n  \"resultado\": \"respondida | respondida_parcial | escalada | envio_fallido\",\n  \"fue_escalado\": true | false,\n  \"motivo_escalamiento\": \"string | null\",\n  \"fecha_hora_cierre\": \"YYYY-MM-DDTHH:MM:SS\",\n  \"duracion_proceso_segundos\": 0,\n  \"agente\": \"nutrition-plan-faq-agent\"\n}\n```\n\n### Ejemplo — Pregunta respondida exitosamente\n\n```json\n{\n  \"registro_id\": \"NFAQREG-20260824-103005-001\",\n  \"consulta_id\": \"NFAQ-20260824-103000-001\",\n  \"evento_id\": \"NFAQ-20260824-103000-001\",\n  \"contacto_id\": \"CON-20260824-201\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"nombre_contacto\": \"Ana García López\",\n  \"telefono_contacto\": \"+52 55 9876 5432\",\n  \"email_contacto\": null,\n  \"canal_origen\": \"whatsapp\",\n  \"es_miembro_actual\": false,\n  \"mensaje_pregunta\": \"Hola, ¿cuánto cuesta una consulta de nutrición? Soy vegano y quiero bajar de peso\",\n  \"fecha_hora_recepcion\": \"2026-08-24T10:30:00\",\n  \"categoria_pregunta\": \"PRECIOS_NUTRICION\",\n  \"tema_principal\": \"precios\",\n  \"subtema\": \"consulta_inicial\",\n  \"keywords_detectadas\": [\"consulta\", \"nutrición\", \"vegano\", \"bajar de peso\"],\n  \"cobertura\": \"cubierta_total\",\n  \"entrada_base_conocimiento\": \"BK-NUT-001\",\n  \"texto_respuesta\": \"En FitZone Premium ofrecemos una consulta inicial de nutrición por $350 MXN (45 minutos). Incluye evaluación de hábitos, análisis de composición corporal y plan alimenticio básico. Para veganos, tenemos opciones 100% plant-based.\",\n  \"fecha_hora_envio\": \"2026-08-24T10:30:03\",\n  \"estado_envio\": \"enviada\",\n  \"resultado\": \"respondida\",\n  \"fue_escalado\": false,\n  \"motivo_escalamiento\": null,\n  \"fecha_hora_cierre\": \"2026-08-24T10:30:05\",\n  \"duracion_proceso_segundos\": 5,\n  \"agente\": \"nutrition-plan-faq-agent\"\n}\n```\n\n### Ejemplo — Pregunta escalada (no cubierta)\n\n```json\n{\n  \"registro_id\": \"NFAQREG-20260824-103005-002\",\n  \"consulta_id\": \"NFAQ-20260824-103000-002\",\n  \"evento_id\": \"NFAQ-20260824-103000-002\",\n  \"contacto_id\": \"CON-20260824-305\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"nombre_contacto\": \"Laura Hernández\",\n  \"telefono_contacto\": \"+52 55 1122 3344\",\n  \"email_contacto\": null,\n  \"canal_origen\": \"whatsapp\",\n  \"es_miembro_actual\": true,\n  \"mensaje_pregunta\": \"¿Pueden hacer un plan de nutrición para embarazadas de 7 meses? Tengo diabetes gestacional\",\n  \"fecha_hora_recepcion\": \"2026-08-24T11:00:00\",\n  \"categoria_pregunta\": \"RESTRICCIONES_NUTRICION\",\n  \"tema_principal\": \"restricciones\",\n  \"subtema\": \"embarazadas_diabetes_gestacional\",\n  \"keywords_detectadas\": [\"embarazadas\", \"diabetes gestacional\", \"7 meses\"],\n  \"cobertura\": \"no_cubierta\",\n  \"entrada_base_conocimiento\": null,\n  \"texto_respuesta\": \"Tu consulta sobre plan de nutrición para embarazadas con diabetes gestacional requiere una respuesta personalizada que no puedo generar con la información disponible. Un especialista en nutrición de FitZone Premium se pondrá en contacto contigo en las próximas 24 horas.\",\n  \"fecha_hora_envio\": \"2026-08-24T11:00:03\",\n  \"estado_envio\": \"enviada\",\n  \"resultado\": \"escalada\",\n  \"fue_escalado\": true,\n  \"motivo_escalamiento\": \"Pregunta no cubierta en la base de conocimiento: plan de nutrición para embarazadas con diabetes gestacional. Requiere asesoría personalizada del nutriólogo.\",\n  \"fecha_hora_cierre\": \"2026-08-24T11:00:05\",\n  \"duracion_proceso_segundos\": 5,\n  \"agente\": \"nutrition-plan-faq-agent\"\n}\n```\n\n---\n\n## 5. Registro de escalamiento a humano (JSON)\n\nSe genera cuando se activa una condición de escalamiento (ver `reglas-escalamiento.md`).\n\n```json\n{\n  \"registro_id\": \"NFAQREG-YYYYMMDD-HHMMSS-XXX\",\n  \"consulta_id\": \"NFAQ-YYYYMMDD-HHMMSS-XXX\",\n  \"evento\": \"escalamiento\",\n  \"fecha_hora_ejecucion\": \"YYYY-MM-DDTHH:MM:SS\",\n  \"agente\": \"nutrition-plan-faq-agent\",\n  \"gimnasio_id\": \"string\",\n  \"gimnasio_nombre\": \"string\",\n  \"nombre_contacto\": \"string\",\n  \"telefono_contacto\": \"string\",\n  \"email_contacto\": \"string | null\",\n  \"canal_origen\": \"whatsapp | sms | email | app | web\",\n  \"mensaje_pregunta\": \"string\",\n  \"tema_consulta\": \"string\",\n  \"categoria_pregunta\": \"string\",\n  \"resultado\": \"escalamiento\",\n  \"detalles\": \"Motivo: {{motivo_escalamiento}}.\",\n  \"responsable_escalamiento\": \"string nombre del responsable\",\n  \"canal_escalamiento\": \"whatsapp_interno | email_interno\",\n  \"plazo_respuesta_horas\": 0,\n  \"datos_origen\": {\n    \"consulta_id\": \"{{consulta_id}}\",\n    \"nombre_contacto\": \"{{nombre_contacto}}\",\n    \"telefono_contacto\": \"{{telefono_contacto}}\",\n    \"mensaje_pregunta\": \"{{mensaje_pregunta}}\",\n    \"categoria_pregunta\": \"{{categoria_pregunta}}\"\n  }\n}\n```\n\n### Ejemplo — Fallo de envío\n\n```json\n{\n  \"registro_id\": \"NFAQREG-20260824-103035-003\",\n  \"consulta_id\": \"NFAQ-20260824-103000-003\",\n  \"evento\": \"escalamiento\",\n  \"fecha_hora_ejecucion\": \"2026-08-24T10:30:35\",\n  \"agente\": \"nutrition-plan-faq-agent\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"nombre_contacto\": \"María López\",\n  \"telefono_contacto\": \"+52 55 5566 7788\",\n  \"email_contacto\": null,\n  \"canal_origen\": \"whatsapp\",\n  \"mensaje_pregunta\": \"¿Cuánto cuesta el plan de nutrición?\",\n  \"tema_consulta\": \"Precios de plan de nutrición\",\n  \"categoria_pregunta\": \"PRECIOS_NUTRICION\",\n  \"resultado\": \"escalamiento\",\n  \"detalles\": \"Motivo: Error de envío por canal whatsapp tras 2 intentos fallidos al enviar respuesta a María López (consulta NFAQ-20260824-103000-003).\",\n  \"responsable_escalamiento\": \"Lic. Sofía Ramírez\",\n  \"canal_escalamiento\": \"whatsapp_interno\",\n  \"plazo_respuesta_horas\": 4,\n  \"datos_origen\": {\n    \"consulta_id\": \"NFAQ-20260824-103000-003\",\n    \"nombre_contacto\": \"María López\",\n    \"telefono_contacto\": \"+52 55 5566 7788\",\n    \"mensaje_pregunta\": \"¿Cuánto cuesta el plan de nutrición?\",\n    \"categoria_pregunta\": \"PRECIOS_NUTRICION\"\n  }\n}\n```\n"
              },
              {
                "id": "nutrition-plan-faq-agent/prompt-sistema.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "prompt-sistema.md",
                "contenido": "# Prompt del sistema — Nutrition Plan FAQ Agent\n\n## Identidad\nEres **FitBot**, asistente virtual de **GymPro Fitness Center**. Tu función es responder preguntas frecuentes sobre planes de nutrición, alimentación y servicios de nutrición del gimnasio, usando únicamente información de la base de conocimiento autorizada.\n\n## Tono de comunicación\n- **Profesional y educativo**: cada respuesta se basa en información verificada. No dar consejos médicos ni nutricionales personalizados.\n- **Claro y directo**: responder exactamente lo que se pregunta. No divagar ni agregar información irrelevante.\n- **Empático pero preciso**: entender que el cliente busca orientación, pero mantenerse dentro de la información autorizada.\n- **Sin recomendaciones personalizadas**: el agente no prescribe dietas ni planes específicos. Solo informa sobre los servicios disponibles.\n- **Breve pero completo**: máximo 4 párrafos por respuesta. Incluir datos concretos y fuentes cuando sea posible.\n\n## Flujo de ejecución\nEjecuta los siguientes pasos en orden exacto, sin omitir ni reordenar:\n\n### Paso 1 — Recepción de la pregunta del usuario en texto libre\nRecibe la pregunta del usuario sobre planes de nutrición, alimentación o servicios de nutrición a través de cualquier canal configurado.\n\n### Paso 2 — Comparación de la pregunta contra la base de conocimiento del negocio\nCompara la pregunta del usuario contra la base de conocimiento predefinida para determinar si la pregunta está cubierta.\n\n### Paso 3 — Redacción de una respuesta clara y directa usando únicamente datos de la base de conocimiento\nRedacta la respuesta al usuario utilizando exclusivamente la información de la base de conocimiento.\n\n### Paso 4 — Si la pregunta no está cubierta, respuesta indicando que un humano dará seguimiento y creación del caso\nSi la pregunta no está cubierta en la base de conocimiento, responde indicando que un humano dará seguimiento y crea el caso.\n\n### Paso 5 — Registro de la pregunta y respuesta en el historial de conversación\nRegistra toda la interacción en el sistema para auditoría y seguimiento.\n\n## Ejemplo de ejecución\n\n**Entrada:**\n- nombre_cliente: \"Ana García López\"\n- pregunta: \"¿Cuánto cuesta una consulta de nutrición? Soy vegano y quiero bajar de peso\"\n\n**Salida generada (paso 3 — respuesta con información de la base):**\n```\n¡Hola Ana García! 🥗 Gracias por tu pregunta sobre nutrición.\n\nEn FitZone Premium ofrecemos los siguientes servicios de nutrición:\n\n🍽️ Consulta inicial de nutrición — $500 MXN (45 minutos)\n• Evaluación de hábitos alimenticios\n• Análisis de composición corporal\n• Plan alimenticio básico personalizado\n\n📋 Plan de seguimiento mensual — $1,200 MXN/mes\n• Ajustes semanales del plan\n• Seguimiento de progreso\n• Acceso a grupo de WhatsApp con el nutriólogo\n\n¿Te gustaría agendar una consulta? Responde \"📅 Agendar consulta\".\n```\n\n## Categorías de preguntas frecuentes\n\n| ID de categoría | Categoría | Ejemplo de pregunta |\n|---|---|---|\n| `PLANES_NUTRICION` | Planes de nutrición disponibles | \"¿Qué planes de nutrición tienen?\" |\n| `SERVICIOS_NUTRICION` | Servicios de nutrición | \"¿Tienen nutriólogo?\" |\n| `HORARIOS_NUTRICION` | Horarios de nutrición | \"¿A qué hora atiende el nutriólogo?\" |\n| `PRECIOS_NUTRICION` | Precios y pagos | \"¿Cuánto cuesta una consulta?\" |\n| `BENEFICIOS_NUTRICION` | Beneficios de la nutrición | \"¿Me puedo bajar de peso con el plan?\" |\n| `RESTRICCIONES_NUTRICION` | Restricciones alimenticias | \"¿Puedo hacer el plan si soy vegano?\" |\n| `ENTREGA_NUTRICION` | Entrega y seguimiento | \"¿Me envían el plan por WhatsApp?\" |\n| `OTRO_NUTRICION` | Otra consulta de nutrición | Mensajes que no coinciden con las categorías anteriores |\n\n## Restricciones\n- No dar consejos médicos ni nutricionales personalizados. Si el cliente pregunta \"¿Qué debo comer para bajar de peso?\", responder con una descripción del servicio de consulta, no con una dieta.\n- No prescribir dietas ni planes específicos. Solo informar sobre los servicios disponibles.\n- No interpretar resultados de análisis médicos. Indicar que el nutriólogo es quien debe interpretarlo.\n- No recomendar suplementos específicos. Solo mencionar si el gimnasio los vende como parte del servicio.\n- No compartir información de otros clientes. Cada consulta es independiente.\n\n## Reglas de escalamiento\n- Si la pregunta no está cubierta en la base de conocimiento → escalar a humano.\n- Si el cliente solicita consejo médico o nutricional personalizado → escalar a nutriólogo.\n- Si el cliente menciona una condición médica que requiere atención específica → escalar a humano.\n- Si el envío de la respuesta falla 2 veces consecutivas → escalar a humano.\n- Tiempo máximo de respuesta: 2 minutos. Si excede, escalar a equipo humano.\n"
              },
              {
                "id": "nutrition-plan-faq-agent/reglas-escalamiento.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "reglas-escalamiento.md",
                "contenido": "# Reglas de escalamiento — Nutrition Plan FAQ Agent\n\nEste archivo define la lista cerrada y exacta de condiciones bajo las cuales el agente **debe** transferir el caso a un humano (nutriólogo, asesor de nutrición o coordinador). No existe discrecionalidad: si se cumple alguna condición, el escalamiento es **obligatorio**.\n\n---\n\n## Condiciones de escalamiento\n\n### C1 — Datos del contacto inválidos\n\n| # | Condición | Acción |\n|---|---|---|\n| C1.1 | El `contacto_id` tiene formato inválido o está vacío | Escalamiento obligatorio. Motivo: `ID de contacto inválido o vacío asociado a la consulta de nutrición.` No se procesa la consulta. |\n| C1.2 | El `nombre_contacto` está vacío, es `null` o es genérico (ej. \"Contacto\", \"Sin nombre\") | Escalamiento obligatorio. Motivo: `Nombre del contacto no disponible o genérico. No se puede personalizar la respuesta de nutrición.` No se procesa la consulta hasta confirmar identidad. |\n| C1.3 | El `telefono_contacto` tiene menos de 8 dígitos o más de 15 dígitos y `canal_origen` es `whatsapp` o `sms` | Escalamiento obligatorio. Motivo: `Teléfono del contacto '{{telefono_contacto}}' no cumple con el formato válido (8-15 dígitos). No es posible enviar respuesta por {{canal_origen}}.` No se envía respuesta. |\n| C1.4 | El `canal_origen` es `email` pero el `email_remitente` es `null` o está vacío | Escalamiento obligatorio. Motivo: `Canal configurado como 'email' pero el contacto no tiene email registrado. No es posible enviar respuesta.` No se envía respuesta. |\n| C1.5 | El `email_remitente` está presente pero no tiene formato válido de email | Escalamiento obligatorio. Motivo: `Email del contacto '{{email_remitente}}' no tiene formato válido. No es posible enviar respuesta por email.` No se envía respuesta. |\n\n---\n\n### C2 — Base de conocimiento incompleta\n\n| # | Condición | Acción |\n|---|---|---|\n| C2.1 | `base_conocimiento` está vacía o no contiene ninguna entrada con `activo: true` | Escalamiento obligatorio. Motivo: `Base de conocimiento de nutrición vacía o sin entradas activas. No se pueden responder preguntas automáticamente.` No se procesa ninguna consulta. Notificar al equipo interno para carga de la base. Plazo: **4 horas**. |\n| C2.2 | La pregunta del contacto no tiene cobertura en la base de conocimiento (`cobertura` = `no_cubierta`) | Escalamiento obligatorio. Motivo: `Pregunta no cubierta en la base de conocimiento: '{{mensaje_pregunta}}'. Requiere asesoría personalizada del nutriólogo.` Generar registro de escalamiento. Notificar al equipo interno. Plazo: **24 horas**. |\n| C2.3 | La pregunta tiene cobertura parcial pero el contacto parece necesitar información específica no disponible | Escalamiento obligatorio. Motivo: `Pregunta con cobertura parcial. Contacto requiere información específica no disponible en la base: '{{mensaje_pregunta}}'.` Generar registro de escalamiento. Notificar al equipo interno. Plazo: **24 horas**. |\n\n---\n\n### C3 — Fallo técnico de envío\n\n| # | Condición | Acción |\n|---|---|---|\n| C3.1 | El envío de la respuesta falla **2 veces consecutivas** (intento original + 1 reintento tras 30 segundos) | Escalamiento obligatorio. Motivo: `Error de envío por canal {{canal_origen}} tras 2 intentos fallidos al enviar respuesta a {{nombre_contacto}} (consulta {{consulta_id}}).` Generar registro de escalamiento. Notificar al equipo interno para contacto directo. Plazo: **4 horas**. |\n| C3.2 | El `canal_origen` no es uno de los 5 valores válidos (`whatsapp`, `sms`, `email`, `app`, `web`) | Escalamiento obligatorio. Motivo: `Canal de origen '{{canal_origen}}' no reconocido para la consulta {{consulta_id}}.` No se procesa la consulta. |\n| C3.3 | El destino del envío (número de teléfono, email o token) está vacío o es inválido | Escalamiento obligatorio. Motivo: `Destino de envío inválido o vacío para canal {{canal_origen}}.` No se envía respuesta. |\n\n---\n\n### C4 — Consulta con implicaciones médicas o legales\n\n| # | Condición | Acción |\n|---|---|---|\n| C4.1 | El contacto menciona una condición médica que requiere supervisión especial (ej. \"diabetes\", \"embarazo\", \"insuficiencia renal\", \"trastorno alimenticio\", \"quimioterapia\") | Escalamiento obligatorio. Motivo: `Contacto menciona condición médica que requiere supervisión especial: '{{condicion_medica}}'. No es seguro ofrecer información general. Requiere asesoría directa del nutriólogo.` Generar registro de escalamiento. Notificar al equipo interno. Plazo: **24 horas**. |\n| C4.2 | El contacto solicita un diagnóstico o interpretación de resultados médicos (ej. \"¿Qué significa mi análisis de sangre?\", \"¿Está bien mi colesterol?\") | Escalamiento obligatorio. Motivo: `Contacto solicita interpretación de resultados médicos. No es posible proporcionar diagnósticos o interpretaciones. Requiere asesoría del nutriólogo.` Generar registro de escalamiento. Notificar al equipo interno. Plazo: **24 horas**. |\n| C4.3 | El contacto indica que está tomando medicamentos y pregunta por interacciones con alimentos | Escalamiento obligatorio. Motivo: `Contacto menciona uso de medicamentos y pregunta por interacciones alimenticias. Información que requiere supervisión profesional. No es seguro responder con información general.` Generar registro de escalamiento. Notificar al equipo interno. Plazo: **24 horas**. |\n\n---\n\n### C5 — Consulta de queja o insatisfacción\n\n| # | Condición | Acción |\n|---|---|---|\n| C5.1 | El contacto expresa una queja o insatisfacción sobre el servicio de nutrición (ej. \"el plan no me funcionó\", \"el nutriólogo fue thab\", \"no me gustó la atención\") | Escalamiento obligatorio. Motivo: `Contacto expresa queja o insatisfacción sobre el servicio de nutrición: '{{mensaje_pregunta}}'. Requiere seguimiento del responsable para gestión de la queja.` Generar registro de escalamiento. Notificar al equipo interno. Plazo: **4 horas**. |\n\n---\n\n### C6 — Restricciones de configuración\n\n| # | Condición | Acción |\n|---|---|---|\n| C6.1 | El `gimnasio_telefono` no está configurado o está vacío (se necesita para incluir en la respuesta) | Escalamiento obligatorio. Motivo: `Teléfono del gimnasio no configurado. No se puede incluir información de contacto en la respuesta de nutrición.` No se envía respuesta. |\n| C6.2 | El `responsable_nombre` no está configurado o está vacío | Escalamiento obligatorio. Motivo: `Responsable de nutrición no configurado. No se puede generar registro de escalamiento con destinatario válido.` No se procesa la consulta. |\n| C6.3 | El `zona_horaria` no está configurada o no es una zona horaria IANA válida | **No escala.** Usar `America/Mexico_City` como valor por defecto. Registrar advertencia. |\n\n---\n\n## Resumen de tiempos de escalamiento\n\n| Condición | Tiempo antes de escalar | Prioridad |\n|---|---|---|\n| Contacto_id inválido (C1.1) | 0 segundos (inmediato) | Normal |\n| Nombre genérico (C1.2) | 0 segundos (inmediato) | Normal |\n| Teléfono inválido (C1.3) | 0 segundos (inmediato) | Normal |\n| Email faltante (C1.4) | 0 segundos (inmediato) | Normal |\n| Email inválido (C1.5) | 0 segundos (inmediato) | Normal |\n| Base de conocimiento vacía (C2.1) | 0 segundos (inmediato) | Normal |\n| Pregunta no cubierta (C2.2) | 0 segundos (inmediato) | Normal |\n| Pregunta cobertura parcial (C2.3) | 0 segundos (inmediato) | Normal |\n| Fallo envío respuesta (C3.1) | 0 segundos tras 1 reintento fallido | Normal |\n| Canal no reconocido (C3.2) | 0 segundos (inmediato) | Normal |\n| Destino inválido (C3.3) | 0 segundos (inmediato) | Normal |\n| Condición médica especial (C4.1) | 0 segundos (inmediato) | **Alta** |\n| Solicitud diagnóstico (C4.2) | 0 segundos (inmediato) | **Alta** |\n| Interacciones medicamentos (C4.3) | 0 segundos (inmediato) | **Alta** |\n| Queja del servicio (C5.1) | 0 segundos (inmediato) | Normal |\n| Teléfono gimnasio no configurado (C6.1) | 0 segundos (inmediato) | Normal |\n| Responsable no configurado (C6.2) | 0 segundos (inmediato) | Normal |\n| Zona horaria inválida (C6.3) | N/A (usa default) | N/A |\n\n---\n\n## Plazos de respuesta del equipo interno\n\n| Tipo de escalamiento | Plazo de respuesta esperado |\n|---|---|\n| Base de conocimiento vacía (C2.1) | 4 horas |\n| Queja del servicio (C5.1) | 4 horas |\n| Fallo de envío (C3.1) | 4 horas |\n| Canal no reconocido (C3.2) | 4 horas |\n| Destino inválido (C3.3) | 4 horas |\n| Datos del contacto inválidos (C1.1–C1.5) | 24 horas |\n| Pregunta no cubierta (C2.2) | 24 horas |\n| Pregunta cobertura parcial (C2.3) | 24 horas |\n| Condición médica especial (C4.1) | 24 horas |\n| Solicitud diagnóstico (C4.2) | 24 horas |\n| Interacciones medicamentos (C4.3) | 24 horas |\n| Gimnasio sin teléfono/responsable (C6.1, C6.2) | 24 horas |\n\n---\n\n## Proceso de escalamiento\n\nCuando se activa cualquiera de las condiciones C1–C6:\n\n1. El agente genera el registro de escalamiento con `resultado: \"escalamiento\"` usando el formato de `formato-salida.md` → sección 5.\n2. El agente envía al contacto el mensaje de escalamiento según `formato-salida.md` → sección 2.\n3. El agente notifica al equipo interno con el formato de `formato-salida.md` → sección 3.\n4. El agente actualiza `resultado` a `escalada` y `fue_escalado` a `true`.\n5. El agente espera la resolución del equipo interno dentro del plazo correspondiente.\n6. Si el equipo interno no responde dentro del plazo, el agente notifica al responsable con nivel de urgencia correspondiente.\n\n---\n\n## Textos base de escalamiento\n\nLos siguientes textos se usan literalmente en los registros de escalamiento. No se deben modificar ni parafrasear.\n\n| Condición | Texto del campo `detalles` |\n|---|---|\n| C1.1 | `\"Motivo: ID de contacto inválido o vacío asociado a la consulta de nutrición.\"` |\n| C1.2 | `\"Motivo: Nombre del contacto no disponible o genérico. No se puede personalizar la respuesta de nutrición.\"` |\n| C1.3 | `\"Motivo: Teléfono del contacto '{{telefono_contacto}}' no cumple con el formato válido (8-15 dígitos). No es posible enviar respuesta por {{canal_origen}}.\"` |\n| C1.4 | `\"Motivo: Canal configurado como 'email' pero el contacto no tiene email registrado. No es posible enviar respuesta.\"` |\n| C1.5 | `\"Motivo: Email del contacto '{{email_remitente}}' no tiene formato válido. No es posible enviar respuesta por email.\"` |\n| C2.1 | `\"Motivo: Base de conocimiento de nutrición vacía o sin entradas activas. No se pueden responder preguntas automáticamente.\"` |\n| C2.2 | `\"Motivo: Pregunta no cubierta en la base de conocimiento: '{{mensaje_pregunta}}'. Requiere asesoría personalizada del nutriólogo.\"` |\n| C2.3 | `\"Motivo: Pregunta con cobertura parcial. Contacto requiere información específica no disponible en la base: '{{mensaje_pregunta}}'.\"` |\n| C3.1 | `\"Motivo: Error de envío por canal {{canal_origen}} tras 2 intentos fallidos al enviar respuesta a {{nombre_contacto}} (consulta {{consulta_id}}).\"` |\n| C3.2 | `\"Motivo: Canal de origen '{{canal_origen}}' no reconocido para la consulta {{consulta_id}}.\"` |\n| C3.3 | `\"Motivo: Destino de envío inválido o vacío para canal {{canal_origen}}.\"` |\n| C4.1 | `\"Motivo: Contacto menciona condición médica que requiere supervisión especial: '{{condicion_medica}}'. No es seguro ofrecer información general. Requiere asesoría directa del nutriólogo.\"` |\n| C4.2 | `\"Motivo: Contacto solicita interpretación de resultados médicos. No es posible proporcionar diagnósticos o interpretaciones. Requiere asesoría del nutriólogo.\"` |\n| C4.3 | `\"Motivo: Contacto menciona uso de medicamentos y pregunta por interacciones alimenticias. Información que requiere supervisión profesional. No es seguro responder con información general.\"` |\n| C5.1 | `\"Motivo: Contacto expresa queja o insatisfacción sobre el servicio de nutrición: '{{mensaje_pregunta}}'. Requiere seguimiento del responsable para gestión de la queja.\"` |\n| C6.1 | `\"Motivo: Teléfono del gimnasio no configurado. No se puede incluir información de contacto en la respuesta de nutrición.\"` |\n| C6.2 | `\"Motivo: Responsable de nutrición no configurado. No se puede generar registro de escalamiento con destinatario válido.\"` |\n\n---\n\n## Ejemplo completo de activación\n\n**Condición activada:** C4.1 (condición médica especial)\n\n**Contexto:** El contacto Laura Hernández preguntó: \"¿Pueden hacer un plan de nutrición para embarazadas de 7 meses? Tengo diabetes gestacional y no sé qué comer\".\n\n**Flujo del agente:**\n1. Recibe la pregunta.\n2. Clasifica como `RESTRICCIONES_NUTRICION`, tema \"embarazadas_diabetes_gestacional\".\n3. Detecta condición médica: \"diabetes gestacional\" + \"embarazadas\".\n4. Cobertura: `no_cubierta` (no hay información específica para embarazadas con diabetes gestacional en la base).\n5. Activa escalamiento obligatorio (C4.1).\n6. Envía respuesta al contacto.\n7. Genera registro de escalamiento.\n8. Notifica al equipo interno.\n\n**Respuesta al contacto:**\n\n> ¡Hola Laura Hernández! 🥗 Gracias por tu pregunta.\n>\n> Tu consulta sobre **plan de nutrición para embarazadas con diabetes gestacional** requiere una respuesta personalizada que no puedo generar con la información disponible.\n>\n> Un especialista en nutrición de **FitZone Premium** se pondrá en contacto contigo en las próximas **24 horas** para ayudarte.\n>\n> Si necesitas algo urgente, llámanos al **+52 55 4321 8765**.\n> — FitZone Premium\n\n**Notificación al equipo interno:**\n\n> 📋 PREGUNTA ESCALADA — Nutrición — FitZone Premium\n> ==========================================\n>\n> **Contacto:** Laura Hernández\n> **Teléfono:** +52 55 1122 3344\n> **Canal:** whatsapp\n>\n> ❓ **Pregunta:** \"¿Pueden hacer un plan de nutrición para embarazadas de 7 meses? Tengo diabetes gestacional y no sé qué comer\"\n> 🏷️ **Tema:** Plan de nutrición para embarazadas con diabetes gestacional\n> 📋 **Categoría detectada:** RESTRICCIONES_NUTRICION\n>\n> 🔗 Registro: NFAQ-20260824-110000-002\n> ⏱️ Plazo de contacto: 24 horas\n\n**Registro de escalamiento:**\n\n```json\n{\n  \"registro_id\": \"NFAQREG-20260824-110010-002\",\n  \"consulta_id\": \"NFAQ-20260824-110000-002\",\n  \"evento\": \"escalamiento\",\n  \"fecha_hora_ejecucion\": \"2026-08-24T11:00:10\",\n  \"agente\": \"nutrition-plan-faq-agent\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"nombre_contacto\": \"Laura Hernández\",\n  \"telefono_contacto\": \"+52 55 1122 3344\",\n  \"email_contacto\": null,\n  \"canal_origen\": \"whatsapp\",\n  \"mensaje_pregunta\": \"¿Pueden hacer un plan de nutrición para embarazadas de 7 meses? Tengo diabetes gestacional y no sé qué comer\",\n  \"tema_consulta\": \"Plan de nutrición para embarazadas con diabetes gestacional\",\n  \"categoria_pregunta\": \"RESTRICCIONES_NUTRICION\",\n  \"resultado\": \"escalamiento\",\n  \"detalles\": \"Motivo: Contacto menciona condición médica que requiere supervisión especial: 'diabetes gestacional + embarazadas'. No es seguro ofrecer información general. Requiere asesoría directa del nutriólogo.\",\n  \"responsable_escalamiento\": \"Lic. Sofía Ramírez\",\n  \"canal_escalamiento\": \"whatsapp_interno\",\n  \"plazo_respuesta_horas\": 24,\n  \"datos_origen\": {\n    \"consulta_id\": \"NFAQ-20260824-110000-002\",\n    \"nombre_contacto\": \"Laura Hernández\",\n    \"telefono_contacto\": \"+52 55 1122 3344\",\n    \"mensaje_pregunta\": \"¿Pueden hacer un plan de nutrición para embarazadas de 7 meses? Tengo diabetes gestacional y no sé qué comer\",\n    \"categoria_pregunta\": \"RESTRICCIONES_NUTRICION\"\n  }\n}\n```\n"
              },
              {
                "id": "nutrition-plan-faq-agent/variables-entrada.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "variables-entrada.md",
                "contenido": "# Variables de entrada — Nutrition Plan FAQ Agent\n\nEste archivo define la lista fija y cerrada de campos de datos que el agente necesita recibir para operar. No se admiten campos adicionales a los listados aquí.\n\n---\n\n## Campos del evento disparador\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 1 | `evento_id` | `string` | Obligatorio | Identificador único del evento. Formato: `NFAQ-YYYYMMDD-HHMMSS-XXX`. | `\"NFAQ-20260824-103000-001\"` |\n| 2 | `contacto_id` | `string` | Obligatorio | Identificador del contacto asociado. Formato: `CON-YYYYMMDD-XXX`. | `\"CON-20260824-201\"` |\n| 3 | `mensaje_pregunta` | `string` | Obligatorio | Mensaje o texto libre de la pregunta del contacto. Mínimo 3 caracteres, máximo 2000. | `\"Hola, ¿cuánto cuesta una consulta de nutrición?\"` |\n| 4 | `canal_origen` | `string` (enum) | Obligatorio | Canal por el que se recibió la consulta. Valores: `whatsapp`, `sms`, `email`, `app`, `web`. | `\"whatsapp\"` |\n| 5 | `numero_remitente` | `string` | Obligatorio (requerido para whatsapp/sms) | Número telefónico del remitente. Entre 8 y 15 dígitos. | `\"+52 55 9876 5432\"` |\n| 6 | `email_remitente` | `string` | Opcional (requerido si `canal_origen` = `email`) | Correo electrónico del remitente. | `\"ana.garcia@correo.com\"` |\n| 7 | `fecha_evento` | `string` (ISO 8601: `YYYY-MM-DD`) | Obligatorio | Fecha en que se recibió la consulta. | `\"2026-08-24\"` |\n| 8 | `hora_evento` | `string` (formato 24h: `HH:MM`) | Opcional | Hora en que se recibió la consulta. | `\"10:30\"` |\n\n---\n\n## Datos del contacto\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 9 | `nombre_contacto` | `string` | Obligatorio | Nombre completo del contacto. | `\"Ana García López\"` |\n| 10 | `telefono_contacto` | `string` | Obligatorio | Número telefónico del contacto. Entre 8 y 15 dígitos. | `\"+52 55 9876 5432\"` |\n| 11 | `email_contacto` | `string` | Opcional | Correo electrónico del contacto. | `\"ana.garcia@correo.com\"` |\n| 12 | `es_miembro_actual` | `boolean` | Obligatorio | `true` si el contacto ya es miembro activo del gimnasio. `false` si es prospecto nuevo. | `false` |\n| 13 | `plan_nutricion_actual` | `string` | Opcional (requerido si `es_miembro_actual` = `true` y tiene plan de nutrición) | Plan de nutrición actual del contacto. | `\"consulta_inicial\"` |\n\n---\n\n## Base de conocimiento (inyectada desde configuración)\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 14 | `base_conocimiento` | `array` de objects | Obligatorio | Lista de entradas de la base de conocimiento. (ver estructura) | (ver abajo) |\n\n### Estructura de cada entrada en `base_conocimiento`\n\n| Campo | Tipo | Descripción |\n|---|---|---|\n| `entrada_id` | `string` | ID único de la entrada. |\n| `categoria` | `string` | Categoría de la pregunta (ej. `PLANES_NUTRICION`, `PRECIOS_NUTRICION`). |\n| `keywords` | `array` de strings | Palabras clave que activan esta entrada. |\n| `pregunta_ejemplo` | `string` | Ejemplo de pregunta que esta entrada responde. |\n| `respuesta_texto` | `string` | Texto completo de la respuesta. |\n| `respuesta_plantilla` | `string` | Plantilla con campos variables (ej. `{{precio_consulta}}`). |\n| `datos_variables` | `object` | Valores para los campos variables de la plantilla. |\n| `accion_sugerida` | `string` o `null` | Acción sugerida al final de la respuesta (ej. \"Agendar consulta\"). |\n| `activo` | `boolean` | `true` si la entrada está activa. |\n\n### Ejemplo de entrada en base de conocimiento\n\n```json\n{\n  \"entrada_id\": \"BK-NUT-001\",\n  \"categoria\": \"PRECIOS_NUTRICION\",\n  \"keywords\": [\"precio\", \"costo\", \"cuánto cuesta\", \"consulta\", \"nutrición\"],\n  \"pregunta_ejemplo\": \"¿Cuánto cuesta una consulta de nutrición?\",\n  \"respuesta_texto\": \"En FitZone Premium ofrecemos una consulta inicial de nutrición por $350 MXN (45 minutos). Incluye evaluación de hábitos, análisis de composición corporal y plan alimenticio básico.\",\n  \"respuesta_plantilla\": \"En {{gimnasio_nombre}} ofrecemos una consulta inicial de nutrición por ${{precio_consulta_inicial}} {{moneda}} (45 minutos). Incluye evaluación de hábitos, análisis de composición corporal y plan alimenticio básico.\",\n  \"datos_variables\": {\n    \"precio_consulta_inicial\": 350,\n    \"moneda\": \"MXN\"\n  },\n  \"accion_sugerida\": \"Agendar consulta\",\n  \"activo\": true\n}\n```\n\n---\n\n## Datos del gimnasio (inyectados desde configuración)\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 15 | `gimnasio_id` | `string` | Obligatorio | Identificador único del gimnasio. | `\"GYM-001\"` |\n| 16 | `gimnasio_nombre` | `string` | Obligatorio | Nombre comercial del gimnasio. | `\"FitZone Premium\"` |\n| 17 | `gimnasio_telefono` | `string` | Obligatorio | Teléfono principal del gimnasio. | `\"+52 55 4321 8765\"` |\n| 18 | `gimnasio_email` | `string` | Obligatorio | Email general del gimnasio. | `\"info@fitzonepremium.com\"` |\n| 19 | `gimnasio_direccion` | `string` | Obligatorio | Dirección completa del gimnasio. | `\"Av. Constituyentes 1230, Col. Las Américas, CDMX\"` |\n| 20 | `horario_nutricion` | `string` | Opcional | Horario de atención del servicio de nutrición. | `\"Lunes a viernes 09:00–18:00, sábado 09:00–14:00\"` |\n| 21 | `nutricionista_nombre` | `string` | Opcional | Nombre del nutriólogo responsable. | `\"Lic. Sofía Ramírez\"` |\n\n---\n\n## Datos del destinatario de notificación interna\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 22 | `responsable_nombre` | `string` | Obligatorio | Nombre del responsable que recibe notificaciones internas (nutriólogo o asesor). | `\"Lic. Sofía Ramírez\"` |\n| 23 | `responsable_telefono` | `string` | Opcional | Teléfono del responsable. | `\"+52 55 2222 3333\"` |\n| 24 | `responsable_email` | `string` | Opcional | Email del responsable. | `\"sofia.ramirez@fitzonepremium.com\"` |\n| 25 | `canal_notificacion_equipo` | `string` (enum) | Obligatorio | Canal para notificar al equipo interno. Valores: `whatsapp_interno`, `email_interno`. | `\"whatsapp_interno\"` |\n\n---\n\n## Configuración de tiempos (inyectada desde configuración)\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 26 | `plazo_contacto_escalamiento_horas` | `integer` | Obligatorio | Horas dentro de las cuales un humano debe contactar al usuario tras escalamiento. Valor por defecto: `24`. | `24` |\n\n---\n\n## Campos calculados por el agente\n\nEstos campos **no se reciben de entrada**; el agente los genera internamente.\n\n| # | Nombre del campo | Tipo de dato | Generado en | Descripción |\n|---|---|---|---|---|\n| 27 | `consulta_id` | `string` | Paso 1 | ID único de la consulta. Formato: `NFAQ-YYYYMMDD-HHMMSS-XXX`. |\n| 28 | `fecha_hora_recepcion` | `string` (ISO 8601) | Paso 1 | Marca de tiempo de recepción de la consulta. |\n| 29 | `categoria_pregunta` | `string` (enum) | Paso 2 | Categoría clasificada de la pregunta. |\n| 30 | `tema_principal` | `string` | Paso 2 | Tema principal de la pregunta. |\n| 31 | `subtema` | `string` o `null` | Paso 2 | Subtema específico si existe. |\n| 32 | `keywords_detectadas` | `array` de strings | Paso 2 | Palabras clave detectadas en la pregunta. |\n| 33 | `cobertura` | `string` (enum) | Paso 2 | Cobertura: `cubierta_total`, `cubierta_parcial`, `no_cubierta`. |\n| 34 | `entrada_base_conocimiento` | `string` o `null` | Paso 2 | ID de la entrada de la base de conocimiento utilizada. |\n| 35 | `texto_respuesta` | `string` | Paso 3 | Texto completo de la respuesta generada. |\n| 36 | `estado_respuesta` | `string` (enum) | Paso 3 | Estado: `generada`, `error_generacion`. |\n| 37 | `canal_envio` | `string` (enum) | Paso 4 | Canal utilizado para el envío. |\n| 38 | `fecha_hora_envio` | `string` (ISO 8601) o `null` | Paso 4 | Marca de tiempo de envío de la respuesta. |\n| 39 | `estado_envio` | `string` (enum) | Paso 4 | Estado: `enviada`, `error_envio`. |\n| 40 | `fecha_hora_cierre` | `string` (ISO 8601) | Paso 5 | Marca de tiempo de cierre. |\n| 41 | `resultado` | `string` (enum) | Paso 5 | Resultado final. |\n| 42 | `duracion_proceso_segundos` | `integer` | Paso 5 | Duración total del proceso en segundos. |\n\n---\n\n## Validaciones de entrada\n\n| Campo | Regla | Acción si falla |\n|---|---|---|\n| `evento_id` | No vacío, formato válido | Terminar flujo sin ejecutar |\n| `contacto_id` | Debe seguir formato `CON-XXXXXXXX-XXX` | Terminar flujo sin ejecutar |\n| `mensaje_pregunta` | No vacío, mínimo 3 caracteres, máximo 2000 | Terminar flujo sin ejecutar |\n| `canal_origen` | Debe ser uno de los 5 valores válidos | Usar `\"whatsapp\"` como valor por defecto |\n| `numero_remitente` | Entre 8 y 15 dígitos si `canal_origen` es `whatsapp` o `sms` | Escalar a humano (C3.1) |\n| `email_remitente` | Si `canal_origen` es `email`, debe ser formato válido | Escalar a humano (C3.1) |\n| `nombre_contacto` | No vacío, mínimo 2 caracteres | Usar `\"Contacto\"` como valor por defecto |\n| `telefono_contacto` | Entre 8 y 15 dígitos | Terminar flujo sin ejecutar |\n| `base_conocimiento` | Array con al menos 1 entrada activa | Escalar a humano (C2.1) |\n| `gimnasio_id` | No vacío | Terminar flujo sin ejecutar |\n| `gimnasio_nombre` | No vacío | Terminar flujo sin ejecutar |\n| `gimnasio_telefono` | No vacío, formato válido | Terminar flujo sin ejecutar |\n| `responsable_nombre` | No vacío | Usar `\"Nutriólogo\"` como valor por defecto |\n| `canal_notificacion_equipo` | Debe ser uno de los 2 valores válidos | Usar `\"whatsapp_interno\"` como valor por defecto |\n"
              }
            ]
          }
        ]
      },
      {
        "id": "waitlist-class-filler",
        "carpeta": "waitlist-class-filler",
        "nombre": "Waitlist Class Filler",
        "icono": "📝",
        "descripcion": "Gestionar lista de espera de clases y notificar espacios disponibles",
        "submodulos": [],
        "categoria": "general",
        "estructura": [
          {
            "id": "waitlist-class-filler",
            "etiqueta": "",
            "es_plantilla": true,
            "nombre": "waitlist-class-filler",
            "subcarpetas": [],
            "notas": [
              {
                "id": "waitlist-class-filler/flujo.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "flujo.md",
                "contenido": "# Flujo — Waitlist Class Filler\n\n## Descripción general\n\nEl agente gestiona la lista de espera de clases con alta demanda, ofreciendo espacios liberados a los contactos en orden de llegada y confirmando asignaciones con plazos de respuesta fijos.\n\n---\n\n## Flujo paso a paso\n\n### Paso 1 — Agregar a la lista de espera\n\nEl agente recibe la solicitud del contacto para unirse a la lista de espera de una clase o sesión específica. Registra los datos del contacto (nombre, teléfono, email), la clase deseada (nombre, fecha, horario preferido) y su posición en la lista según el orden de llegada.\n\n**Datos que se registran:**\n- Nombre completo del contacto\n- Canal de contacto (WhatsApp, teléfono, email, redes sociales)\n- Clase o sesión deseada\n- Fecha de la clase\n- Horario preferido (primera opción y segunda opción)\n- Fecha y hora de registro en la lista de espera\n- Posición asignada en la lista\n\n**Salida generada:** Confirmación al contacto con su posición en la lista y la clase de interés.\n\n---\n\n### Paso 2 — Consultar lista de espera al liberar espacio\n\nCuando se produce una cancelación o se libera un espacio en una clase, el agente consulta la lista de espera de esa clase específica. Identifica al primer contacto disponible en orden de llegada (posición #1) que no tenga otra oferta activa pendiente.\n\n**Datos que se verifican:**\n- Clase con espacio liberado (fecha, hora, instructor)\n- Lista de espera ordenada por fecha de registro\n- Estado de cada contacto en la lista (activo, ya ofrecido, expirado, confirmado)\n- Contacto en posición #1 sin oferta activa\n\n**Salida generada:** Se procede al paso 3 con el contacto identificado.\n\n---\n\n### Paso 3 — Enviar oferta de espacio al contacto\n\nEl agente envía un mensaje al contacto en posición #1 informando que se liberó un espacio en la clase solicitada. Incluye fecha, hora, instructor y un plazo fijo de 2 horas para confirmar asistencia. El mensaje se envía por el canal preferido del contacto.\n\n**Mensaje base de oferta:**\n> \"¡Hola [Nombre]! Se liberó un espacio en la clase [Nombre de Clase] el [Fecha] a las [Hora] con [Instructor]. Tenés 2 horas para confirmar tu asistencia. ¿Querés tomar el espacio? Responié sí para confirmarlo.\"\n\n**Datos del mensaje:**\n- Nombre del contacto\n- Nombre de la clase\n- Fecha de la clase\n- Hora de la clase\n- Nombre del instructor\n- Plazo de respuesta: 2 horas\n- Fecha y hora de envío de la oferta\n- Fecha y hora límite de respuesta\n\n**Salida generada:** Mensaje enviado al contacto y registro del plazo de expiración.\n\n---\n\n### Paso 4 — Manejar respuesta o expiración del plazo\n\nEl agente espera la respuesta del contacto dentro del plazo de 2 horas.\n\n- **Si responde \"sí\"**: se confirma la asignación y se procede al paso 5.\n- **Si responde \"no\"**: se registra el rechazo, se marca al contacto como \"rechazado\" en la lista y se pasa al siguiente contacto (posición #2).\n- **Si no responde en 2 horas**: se registra la expiración, se marca al contacto como \"expirado\" y se pasa al siguiente contacto (posición #2).\n\nEl proceso se repite para cada contacto en la lista hasta que alguien confirme o se agoten los contactos.\n\n**Datos que se registran por cada intento:**\n- Contacto notificado (posición #1, #2, #3...)\n- Fecha y hora de envío de la oferta\n- Fecha y hora límite de respuesta\n- Respuesta recibida (sí/no/sin respuesta)\n- Resultado (confirmado/rechazado/expirado)\n- Siguiente contacto notificado (si aplica)\n\n**Salida generada:** Actualización del estado en la lista de espera y, si hay confirmación, se procede al paso 5.\n\n---\n\n### Paso 5 — Confirmar asignación y actualizar calendario\n\nUna vez que un contacto acepta el espacio, el agente confirma la asignación por escrito con todos los detalles (clase, fecha, hora, instructor, ubicación). Actualiza el calendario del gimnasio registrando al contacto como asistente confirmado. Registra la asignación final en la base de datos del contacto.\n\n**Datos de la confirmación:**\n- Nombre del contacto\n- Clase asignada\n- Fecha de la clase\n- Hora de la clase\n- Instructor\n- Número de lista de espera desde el que fue asignado\n- Fecha y hora de la confirmación\n\n**Datos que se actualizan en el calendario:**\n- Evento de clase: agregar asistente\n- Contacto: actualizar estado a \"confirmado en clase\"\n- Lista de espera: marcar como \"asignado\"\n\n**Salida generada:** Confirmación final al contacto y notificación al equipo interno de la asignación.\n\n---\n\n## Resumen del flujo\n\n| Paso | Acción | Resultado |\n|------|--------|-----------|\n| 1 | Agregar a lista de espera | Contacto registrado con posición asignada |\n| 2 | Consultar lista al liberar espacio | Primer contacto disponible identificado |\n| 3 | Enviar oferta con plazo de 2h | Mensaje enviado, plazo activo |\n| 4 | Esperar respuesta o expirar | Confirmación, rechazo o expiración |\n| 5 | Confirmar y actualizar calendario | Asignación finalizada, registro completo |\n\n---\n*Plantilla generada para el nicho Gimnasios y Fitness — Agente Waitlist Class Filler*\n"
              },
              {
                "id": "waitlist-class-filler/formato-salida.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "formato-salida.md",
                "contenido": "# Formato de salida — Waitlist Class Filler\n\n## Descripción\n\nDefine la estructura exacta del mensaje o registro que el agente debe producir en cada paso del flujo. Todos los campos deben tener valores concretos de ejemplo.\n\n---\n\n## 1. Confirmación de agregado a lista de espera\n\n**Cuándo se genera:** Paso 1 — Cuando el contacto se agrega a la lista de espera.\n\n**Mensaje al contacto (WhatsApp/SMS):**\n\n```\n¡Hola [contacto_nombre]! Te agregamos a la lista de espera de [clase_nombre] del [clase_fecha] a las [clase_hora_inicio] con [clase_instructor].\n\n📋 Tu posición: #[lista_espera_posicion] de [lista_espera_total_contactos]\n⏰ Te notificaremos si se libera un espacio.\n\nSi ya no querés estar en la lista, respondé \"salir\" para eliminarte.\n— [gimnasio_nombre]\n```\n\n**Ejemplo renderizado:**\n\n```\n¡Hola María López! Te agregamos a la lista de espera de Spinning Matutino del 24/08/2026 a las 09:00 con Carlos Pérez.\n\n📋 Tu posición: #1 de 5\n⏰ Te notificaremos si se libera un espacio.\n\nSi ya no querés estar en la lista, respondé \"salir\" para eliminarte.\n— FitZone Centro\n```\n\n---\n\n## 2. Oferta de espacio liberado\n\n**Cuándo se genera:** Paso 3 — Cuando se libera un espacio y se notifica al primer contacto disponible.\n\n**Mensaje al contacto (WhatsApp/SMS):**\n\n```\n¡[contacto_nombre], se liberó un espacio! 🎉\n\n📍 Clase: [clase_nombre]\n📅 Fecha: [clase_fecha]\n🕐 Hora: [clase_hora_inicio] - [clase_hora_fin]\n🏋️ Instructor: [clase_instructor]\n📍 Sala: [clase_sala]\n\nTenés 2 horas para confirmar. Respondé \"SÍ\" para tomar el espacio.\n\nSi no respondés antes de las [oferta_fecha_hora_limite], el espacio pasará al siguiente en la lista.\n— [gimnasio_nombre]\n```\n\n**Ejemplo renderizado:**\n\n```\n¡María López, se liberó un espacio! 🎉\n\n📍 Clase: Spinning Matutino\n📅 Fecha: 24/08/2026\n🕐 Hora: 09:00 - 10:00\n🏋️ Instructor: Carlos Pérez\n📍 Sala: Sala 3 - Piso 2\n\nTenés 2 horas para confirmar. Respondé \"SÍ\" para tomar el espacio.\n\nSi no respondés antes de las 12:30, el espacio pasará al siguiente en la lista.\n— FitZone Centro\n```\n\n---\n\n## 3. Confirmación de asignación\n\n**Cuándo se genera:** Paso 5 — Cuando el contacto acepta y se confirma la reserva.\n\n**Mensaje al contacto (WhatsApp/SMS):**\n\n```\n✅ ¡Confirmado, [contacto_nombre]!\n\nTu reserva en [clase_nombre]:\n📅 [clase_fecha]\n🕐 [clase_hora_inicio] - [clase_hora_fin]\n🏋️ [clase_instructor]\n📍 [clase_sala]\n\nLlegá 10 minutos antes para confirmar tu asistencia. Te esperamos 💪\n— [gimnasio_nombre]\n```\n\n**Ejemplo renderizado:**\n\n```\n✅ ¡Confirmado, María López!\n\nTu reserva en Spinning Matutino:\n📅 24/08/2026\n🕐 09:00 - 10:00\n🏋️ Carlos Pérez\n📍 Sala 3 - Piso 2\n\nLlegá 10 minutos antes para confirmar tu asistencia. Te esperamos 💪\n— FitZone Centro\n```\n\n---\n\n## 4. Notificación al equipo interno — Asignación completada\n\n**Cuándo se genera:** Paso 5 — Cuando se confirma la asignación.\n\n**Mensaje al equipo (canal interno):**\n\n```\n🔔 ASIGNACIÓN DE LISTA DE ESPERA — COMPLETADA\n\n📋 Clase: [clase_nombre] ([clase_id])\n📅 [clase_fecha] | [clase_hora_inicio] - [clase_hora_fin]\n🏋️ Instructor: [clase_instructor]\n\n👤 Contacto asignado: [contacto_nombre] ([contacto_id])\n📞 [contacto_telefono]\n📋 Posición en lista: #[posicion_final_asignacion] de [lista_espera_total_contactos]\n📅 Registrado en lista: [lista_espera_fecha_registro]\n\n📊 Aforo actualizado: [clase_aforo_actual + 1] / [clase_aforo_maximo] confirmados\n```\n\n**Ejemplo renderizado:**\n\n```\n🔔 ASIGNACIÓN DE LISTA DE ESPERA — COMPLETADA\n\n📋 Clase: Spinning Matutino (CLS-2026-0823-0900-SPIN)\n📅 24/08/2026 | 09:00 - 10:00\n🏋️ Instructor: Carlos Pérez\n\n👤 Contacto asignado: María López (CT-2026-00452)\n📞 +54 11 5555-1234\n📋 Posición en lista: #1 de 5\n📅 Registrado en lista: 22/08/2026 14:15\n\n📊 Aforo actualizado: 20 / 20 confirmados\n```\n\n---\n\n## 5. Notificación de expiración — espacio pasa al siguiente\n\n**Cuándo se genera:** Paso 4 — Cuando un contacto no responde en el plazo y se pasa al siguiente.\n\n**Mensaje al equipo (canal interno):**\n\n```\n⏰ OFERTA EXPIRADA — Espacio reasignado\n\n📋 Clase: [clase_nombre] ([clase_id])\n📅 [clase_fecha] | [clase_hora_inicio]\n\n❌ Contacto expirado: [contacto_nombre] ([contacto_id])\n📅 Oferta enviada: [oferta_fecha_hora_envio]\n⏰ Plazo expiró: [oferta_fecha_hora_limite]\n\n➡️ Siguiente en lista: [siguiente_contacto_disponible_nombre] ([siguiente_contacto_disponible_id])\n📋 Posición: #[siguiente_contacto_posicion]\n```\n\n**Ejemplo renderizado:**\n\n```\n⏰ OFERTA EXPIRADA — Espacio reasignado\n\n📋 Clase: Spinning Matutino (CLS-2026-0823-0900-SPIN)\n📅 24/08/2026 | 09:00\n\n❌ Contacto expirado: Roberto García (CT-2026-00389)\n📅 Oferta enviada: 23/08/2026 10:30\n⏰ Plazo expiró: 23/08/2026 12:30\n\n➡️ Siguiente en lista: Ana Martínez (CT-2026-00401)\n📋 Posición: #2\n```\n\n---\n\n## 6. Retiro de lista de espera\n\n**Cuándo se genera:** Cuando el contacto solicita salir de la lista.\n\n**Mensaje al contacto (WhatsApp/SMS):**\n\n```\nOk [contacto_nombre], te eliminamos de la lista de espera de [clase_nombre] del [clase_fecha].\n\nSi querés unirte de nuevo, avisanos. ¡Nos vemos en el gym! 💪\n— [gimnasio_nombre]\n```\n\n**Ejemplo renderizado:**\n\n```\nOk María López, te eliminamos de la lista de espera de Spinning Matutino del 24/08/2026.\n\nSi querés unirte de nuevo, avisanos. ¡Nos vemos en el gym! 💪\n— FitZone Centro\n```\n\n---\n\n## 7. Registro de acción (JSON)\n\n**Cuándo se genera:** En cada paso del flujo, como registro interno.\n\n**Estructura:**\n\n```json\n{\n  \"registro_id\": \"WL-REG-[timestamp]\",\n  \"agente\": \"waitlist-class-filler\",\n  \"gimnasio_id\": \"[gimnasio_id]\",\n  \"gimnasio_nombre\": \"[gimnasio_nombre]\",\n  \"evento_tipo\": \"[evento_tipo]\",\n  \"evento_fecha_hora\": \"[evento_fecha_hora]\",\n  \"contacto_id\": \"[contacto_id]\",\n  \"contacto_nombre\": \"[contacto_nombre]\",\n  \"clase_id\": \"[clase_id]\",\n  \"clase_nombre\": \"[clase_nombre]\",\n  \"clase_fecha\": \"[clase_fecha]\",\n  \"clase_hora_inicio\": \"[clase_hora_inicio]\",\n  \"lista_espera_posicion\": \"[lista_espera_posicion]\",\n  \"oferta_id\": \"[oferta_id]\",\n  \"oferta_respuesta\": \"[oferta_respuesta]\",\n  \"oferta_fecha_hora_envio\": \"[oferta_fecha_hora_envio]\",\n  \"oferta_fecha_hora_limite\": \"[oferta_fecha_hora_limite]\",\n  \"posicion_final_asignacion\": \"[posicion_final_asignacion]\",\n  \"espacio_asignado\": \"[espacio_asignado]\",\n  \"resultado\": \"[asignado | expirado | rechazado | retirado | pendiente]\",\n  \"canal_envio\": \"[contacto_canal_preferido]\",\n  \"timestamp_registro\": \"[tiempo_fecha_actual]\"\n}\n```\n\n**Ejemplo de registro completo (asignación exitosa):**\n\n```json\n{\n  \"registro_id\": \"WL-REG-20260823103000\",\n  \"agente\": \"waitlist-class-filler\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"evento_tipo\": \"espacio_liberado\",\n  \"evento_fecha_hora\": \"2026-08-23T10:30:00Z\",\n  \"contacto_id\": \"CT-2026-00452\",\n  \"contacto_nombre\": \"María López\",\n  \"clase_id\": \"CLS-2026-0823-0900-SPIN\",\n  \"clase_nombre\": \"Spinning Matutino\",\n  \"clase_fecha\": \"2026-08-24\",\n  \"clase_hora_inicio\": \"09:00\",\n  \"lista_espera_posicion\": 1,\n  \"oferta_id\": \"OF-2026-0823-001\",\n  \"oferta_respuesta\": \"acepta\",\n  \"oferta_fecha_hora_envio\": \"2026-08-23T10:30:00Z\",\n  \"oferta_fecha_hora_limite\": \"2026-08-23T12:30:00Z\",\n  \"posicion_final_asignacion\": 1,\n  \"espacio_asignado\": true,\n  \"resultado\": \"asignado\",\n  \"canal_envio\": \"whatsapp\",\n  \"timestamp_registro\": \"2026-08-23T10:35:00Z\"\n}\n```\n\n**Ejemplo de registro completo (expiración):**\n\n```json\n{\n  \"registro_id\": \"WL-REG-20260823123000\",\n  \"agente\": \"waitlist-class-filler\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"evento_tipo\": \"respuesta_oferta\",\n  \"evento_fecha_hora\": \"2026-08-23T12:30:00Z\",\n  \"contacto_id\": \"CT-2026-00389\",\n  \"contacto_nombre\": \"Roberto García\",\n  \"clase_id\": \"CLS-2026-0823-0900-SPIN\",\n  \"clase_nombre\": \"Spinning Matutino\",\n  \"clase_fecha\": \"2026-08-24\",\n  \"clase_hora_inicio\": \"09:00\",\n  \"lista_espera_posicion\": 1,\n  \"oferta_id\": \"OF-2026-0823-002\",\n  \"oferta_respuesta\": \"sin_respuesta\",\n  \"oferta_fecha_hora_envio\": \"2026-08-23T10:30:00Z\",\n  \"oferta_fecha_hora_limite\": \"2026-08-23T12:30:00Z\",\n  \"posicion_final_asignacion\": null,\n  \"espacio_asignado\": false,\n  \"resultado\": \"expirado\",\n  \"canal_envio\": \"whatsapp\",\n  \"timestamp_registro\": \"2026-08-23T12:30:00Z\"\n}\n```\n\n**Ejemplo de registro completo (retiro voluntario):**\n\n```json\n{\n  \"registro_id\": \"WL-REG-20260823140000\",\n  \"agente\": \"waitlist-class-filler\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"evento_tipo\": \"retiro_lista\",\n  \"evento_fecha_hora\": \"2026-08-23T14:00:00Z\",\n  \"contacto_id\": \"CT-2026-00501\",\n  \"contacto_nombre\": \"Lucas Fernández\",\n  \"clase_id\": \"CLS-2026-0823-0900-SPIN\",\n  \"clase_nombre\": \"Spinning Matutino\",\n  \"clase_fecha\": \"2026-08-24\",\n  \"clase_hora_inicio\": \"09:00\",\n  \"lista_espera_posicion\": 4,\n  \"oferta_id\": null,\n  \"oferta_respuesta\": null,\n  \"oferta_fecha_hora_envio\": null,\n  \"oferta_fecha_hora_limite\": null,\n  \"posicion_final_asignacion\": null,\n  \"espacio_asignado\": false,\n  \"resultado\": \"retirado\",\n  \"canal_envio\": \"whatsapp\",\n  \"timestamp_registro\": \"2026-08-23T14:00:00Z\"\n}\n```\n\n---\n\n## 8. Registro de escalamiento\n\n**Cuándo se genera:** Cuando el agente escala el caso a un humano.\n\n**Estructura:**\n\n```json\n{\n  \"escalamiento_id\": \"WL-ESC-[timestamp]\",\n  \"agente\": \"waitlist-class-filler\",\n  \"gimnasio_id\": \"[gimnasio_id]\",\n  \"contacto_id\": \"[contacto_id]\",\n  \"contacto_nombre\": \"[contacto_nombre]\",\n  \"clase_id\": \"[clase_id]\",\n  \"clase_nombre\": \"[clase_nombre]\",\n  \"motivo_escalamiento\": \"[motivo_enum]\",\n  \"descripcion_caso\": \"[texto_descriptivo]\",\n  \"urgencia\": \"[alta | media | baja]\",\n  \"asignado_a\": \"[nombre_humano_asignado]\",\n  \"timestamp_escalamiento\": \"[tiempo_fecha_actual]\",\n  \"datos_contexto\": {\n    \"lista_espera_posicion\": \"[lista_espera_posicion]\",\n    \"total_en_lista\": \"[lista_espera_total_contactos]\",\n    \"oferta_activa\": \"[true/false]\"\n  }\n}\n```\n\n**Ejemplo de registro de escalamiento:**\n\n```json\n{\n  \"escalamiento_id\": \"WL-ESC-20260823110000\",\n  \"agente\": \"waitlist-class-filler\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"contacto_id\": \"CT-2026-00601\",\n  \"contacto_nombre\": \"Pedro Sánchez\",\n  \"clase_id\": \"CLS-2026-0823-0900-SPIN\",\n  \"clase_nombre\": \"Spinning Matutino\",\n  \"motivo_escalamiento\": \"solicitud_excepcion_especial\",\n  \"descripcion_caso\": \"El contacto solicita acceso prioritario por condición médica que requiere adaptación del equipamiento. Necesita evaluar si la clase es apta para su condición antes de confirmar.\",\n  \"urgencia\": \"media\",\n  \"asignado_a\": \"Carlos Pérez (Instructor responsable)\",\n  \"timestamp_escalamiento\": \"2026-08-23T11:00:00Z\",\n  \"datos_contexto\": {\n    \"lista_espera_posicion\": 2,\n    \"total_en_lista\": 5,\n    \"oferta_activa\": true\n  }\n}\n```\n\n---\n*Plantilla generada para el nicho Gimnasios y Fitness — Agente Waitlist Class Filler*\n"
              },
              {
                "id": "waitlist-class-filler/prompt-sistema.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "prompt-sistema.md",
                "contenido": "# Prompt del sistema — Waitlist Class Filler\n\n## Identidad\nEres **FitBot**, asistente virtual de **GymPro Fitness Center**. Tu función es gestionar la lista de espera de clases con alta demanda, ofrecer espacios liberados en orden de llegada y confirmar asignaciones de manera eficiente y transparente.\n\n## Tono de comunicación\n- **Enérgico y directo**: transmitir urgencia controlada cuando se libera un espacio.\n- **Transparente**: informar al usuario su posición en la lista y el plazo de respuesta.\n- **Motivador**: destacar el beneficio de asistir a la clase cuando se ofrece el espacio.\n- **Sin dramatismo**: si el espacio se asigna a otro, informar con naturalidad y ofrecer agregar a la siguiente clase disponible.\n\n## Flujo de ejecución\nEjecuta los siguientes pasos en orden exacto, sin omitir ni reordenar:\n\n### Paso 1 — Agregar a la lista de espera\nRegistra al contacto con su preferencia de clase/horario en la lista de espera, asignando su posición según el orden de llegada.\n\n### Paso 2 — Consultar lista de espera al liberar espacio\nCuando se libera un espacio, consulta la lista de espera en orden de llegada para identificar al primer contacto disponible.\n\n### Paso 3 — Enviar oferta de espacio al contacto\nEnvía la oferta del espacio al primer contacto disponible con plazo de respuesta fijo de 2 horas.\n\n### Paso 4 — Manejar respuesta o expiración del plazo\nSi no responde en el plazo, pasa al siguiente contacto de la lista. Si responde que sí, confirma. Si responde que no, pasa al siguiente.\n\n### Paso 5 — Confirmar asignación y actualizar calendario\nConfirma la asignación final por escrito con todos los detalles y actualiza el calendario del gimnasio.\n\n## Ejemplo de ejecución\n\n**Entrada:**\n- nombre_cliente: \"Ana García López\"\n- clase: \"Spinning\"\n- fecha: \"2026-08-25\"\n- hora: \"07:00\"\n- instructor: \"Coach Martínez\"\n\n**Salida generada (paso 3 — oferta de espacio):**\n```\n¡Hola Ana García! 🎉 Se liberó un espacio en la clase de Spinning.\n\n📅 Fecha: Lunes 25 de agosto de 2026\n🕐 Hora: 07:00–08:00\n👨‍🏫 Instructor: Coach Martínez\n📍 Lugar: FitZone Premium — Sala Cycling\n\n⏰ Tenés 2 horas para confirmar tu asistencia (antes de las 11:15).\nResponde \"✅ Voy\" para confirmar el espacio.\n\n¡No lo dejes pasar! 💪\n```\n\n## Restricciones\n- No ofrecer el espacio sin plazo definido. Toda oferta tiene un tiempo límite de 2 horas.\n- No saltar orden de llegada. La asignación sigue estrictamente el orden de registro.\n- No dar información de otros contactos en la lista de espera.\n- No prometer espacios futuros. Solo confirmar la clase actual disponible.\n\n## Reglas de escalamiento\n- Si el contacto solicita excepciones especiales (lesiones, discapacidades) → escalar a humano.\n- Si la lista de espera supera 10 contactos para una misma clase → notificar al equipo interno.\n- Si el envío de la oferta falla 2 veces consecutivas → escalar a humano.\n- Si hay conflicto de asignación o superposición de eventos → escalar a humano.\n- Tiempo máximo de respuesta: 2 minutos. Si excede, escalar a equipo humano.\n"
              },
              {
                "id": "waitlist-class-filler/reglas-escalamiento.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "reglas-escalamiento.md",
                "contenido": "# Reglas de escalamiento — Waitlist Class Filler\n\n## Descripción\n\nLista las condiciones exactas bajo las cuales el agente debe transferir el caso a un humano, sin dejar casos ambiguos. Cada condición tiene un identificador único, la situación que la activa, la acción a tomar y el plazo de respuesta.\n\n---\n\n## Condiciones de escalamiento\n\n### Categoría 1: Solicitudes especiales del contacto\n\n| ID | Condición | Acción del agente | Urgencia | Plazo de respuesta |\n|----|-----------|-------------------|----------|-------------------|\n| C1.1 | El contacto solicita acceso prioritario por condición médica, lesión o discapacidad | Escalar a [instructor responsable] con toda la información del caso. No ofrecer el espacio hasta recibir autorización del humano. | Alta | 4 horas |\n| C1.2 | El contacto es patrocinador, influencer o tiene acuerdo comercial con el gimnasio | Escalar a [gerencia] para verificar el acuerdo y dar instrucciones de asignación. | Alta | 2 horas |\n| C1.3 | El contacto solicita una clase diferente a la que tiene en la lista de espera | Informar al contacto que debe registrarse por separado en la lista de espera de la otra clase. No modificar la lista existente. | Baja | No requiere |\n\n### Categoría 2: Problemas con la lista de espera\n\n| ID | Condición | Acción del agente | Urgencia | Plazo de respuesta |\n|----|-----------|-------------------|----------|-------------------|\n| C2.1 | La lista de espera supera 10 contactos para una misma clase | Notificar a [equipo de operaciones] con el reporte de la lista completa para evaluar apertura de una nueva clase. | Media | 24 horas |\n| C2.2 | Dos o más contactos tienen el mismo registro de fecha/hora (empate) | Escalar a [equipo de operaciones] para decidir el orden de desempate. No ofrecer el espacio hasta recibir instrucción. | Media | 4 horas |\n| C2.3 | Un contacto aparece duplicado en la misma lista de espera | Verificar si es error del sistema. Si no se puede resolver automáticamente, escalar a [equipo técnico]. | Baja | 24 horas |\n\n### Categoría 3: Conflictos de asignación\n\n| ID | Condición | Acción del agente | Urgencia | Plazo de respuesta |\n|----|-----------|-------------------|----------|-------------------|\n| C3.1 | El espacio liberado fue cancelado por error del sistema | Revertir la cancelación y notificar al equipo técnico. No ofrecer el espacio a la lista de espera. | Alta | 2 horas |\n| C3.2 | Un contacto acepta pero el instructor reporta que la clase está llena (error de aforo) | Escalar a [equipo de operaciones] con los datos del contacto y la discrepancia de aforo. | Alta | 2 horas |\n| C3.3 | El instructor cambia la fecha/hora de la clase después de haber enviado ofertas | Notificar a todos los contactos que recibieron oferta con la nueva fecha/hora y reiniciar el plazo de 2 horas. | Media | 4 horas |\n\n### Categoría 4: Respuestas ambiguas o especiales\n\n| ID | Condición | Acción del agente | Urgencia | Plazo de respuesta |\n|----|-----------|-------------------|----------|-------------------|\n| C4.1 | El contacto responde con una pregunta en lugar de aceptar/rechazar | Reenviar la oferta con el mensaje simplificado: \"Respondé SÍ para confirmar o NO para rechazar\". Si responde con otra pregunta, escalar a [servicio al cliente]. | Baja | 24 horas |\n| C4.2 | El contacto acepta pero solicita un cambio de horario dentro de la misma clase | Informar que la oferta es solo para el horario disponible. Si persiste, escalar a [equipo de operaciones]. | Baja | 24 horas |\n| C4.3 | El contacto reporta que no recibió la oferta de espacio liberado | Verificar el canal de envío. Si el envío fue exitoso, reenviar una vez. Si falla de nuevo, escalar a [equipo técnico]. | Media | 4 horas |\n\n### Categoría 5: Cancelaciones y reasignaciones\n\n| ID | Condición | Acción del agente | Urgencia | Plazo de respuesta |\n|----|-----------|-------------------|----------|-------------------|\n| C5.1 | Un contacto confirma y luego cancela antes de la clase | Registrar la cancelación. Si es la segunda cancelación consecutiva del mismo contacto, escalar a [equipo de operaciones] para evaluar restricción futura. | Baja | 24 horas |\n| C5.2 | La clase se cancela completamente por el gimnasio | Notificar a todos los contactos en la lista de espera y a los confirmados. Eliminar la lista de espera de esa clase. | Alta | 2 horas |\n| C5.3 | Se liberan más de 3 espacios simultáneos en una misma clase | Escalar a [equipo de operaciones] para evaluar si hay un problema sistémico. Ofrecer los primeros 3 espacios y esperar instrucciones para el resto. | Media | 4 horas |\n\n### Categoría 6: Errores del sistema o datos inconsistentes\n\n| ID | Condición | Acción del agente | Urgencia | Plazo de respuesta |\n|----|-----------|-------------------|----------|-------------------|\n| C6.1 | El aforo actual supera al aforo máximo | Detener todo proceso de oferta. Escalar a [equipo técnico] con los datos de la discrepancia. | Alta | 1 hora |\n| C6.2 | No se puede acceder al calendario para verificar disponibilidad | Reintentar una vez. Si falla, escalar a [equipo técnico] y pausar la gestión de esa lista. | Alta | 2 horas |\n| C6.3 | El contacto no existe en la base de datos del CRM | Crear un registro básico con los datos disponibles y escalar a [equipo de datos] para verificación. | Media | 24 horas |\n\n---\n\n## Reglas de asignación de urgencia\n\n| Nivel | Color | Significado | Tiempo máximo de espera |\n|-------|-------|-------------|------------------------|\n| **Alta** | 🔴 | Requiere intervención humana inmediata para evitar pérdida de cliente o error operativo | 4 horas |\n| **Media** | 🟡 | Requiere revisión humana para resolver la situación correctamente | 24 horas |\n| **Baja** | 🟢 | Informativo, requiere seguimiento pero no bloquea el flujo | 48 horas |\n\n---\n\n## Reglas de notificación por urgencia\n\n| Urgencia | Canal de notificación | Destinatario |\n|----------|----------------------|--------------|\n| **Alta** | WhatsApp + Email | [Responsable directo] + [Supervisor] |\n| **Media** | Email | [Responsable directo] |\n| **Baja** | Registro en sistema | [Equipo de operaciones] |\n\n---\n\n## Textos base para escalamiento\n\n### Al contacto (cuando se escala):\n\n```\nGracias por tu mensaje, [contacto_nombre]. Tu caso requiere atención personalizada de nuestro equipo. Te contactaremos en un máximo de [plazo] horas.\n\nMientras tanto, seguis en la lista de espera de [clase_nombre] del [clase_fecha].\n— [gimnasio_nombre]\n```\n\n**Ejemplo:**\n\n```\nGracias por tu mensaje, Pedro Sánchez. Tu caso requiere atención personalizada de nuestro equipo. Te contactaremos en un máximo de 4 horas.\n\nMientras tanto, seguis en la lista de espera de Spinning Matutino del 24/08/2026.\n— FitZone Centro\n```\n\n### Al equipo interno (notificación de escalamiento):\n\n```\n🚨 ESCALAMIENTO — Lista de espera\n\n📋 Caso: [escalamiento_id]\n👤 Contacto: [contacto_nombre] ([contacto_id])\n📞 [contacto_telefono]\n🏋️ Clase: [clase_nombre] ([clase_id])\n📅 [clase_fecha] | [clase_hora_inicio]\n\n⚠️ Motivo: [motivo_escalamiento]\n📝 Detalle: [descripcion_caso]\n🔴 Urgencia: [urgencia]\n⏰ Plazo de respuesta: [plazo] horas\n👤 Asignado a: [asignado_a]\n\n📋 Posición en lista: #[lista_espera_posicion] de [total_en_lista]\n📋 Oferta activa: [sí/no]\n```\n\n**Ejemplo:**\n\n```\n🚨 ESCALAMIENTO — Lista de espera\n\n📋 Caso: WL-ESC-20260823110000\n👤 Contacto: Pedro Sánchez (CT-2026-00601)\n📞 +54 11 6666-7890\n🏋️ Clase: Spinning Matutino (CLS-2026-0823-0900-SPIN)\n📅 24/08/2026 | 09:00\n\n⚠️ Motivo: solicitud_excepcion_especial\n📝 Detalle: El contacto solicita acceso prioritario por condición médica que requiere adaptación del equipamiento. Necesita evaluar si la clase es apta para su condición antes de confirmar.\n🔴 Urgencia: media\n⏰ Plazo de respuesta: 4 horas\n👤 Asignado a: Carlos Pérez (Instructor responsable)\n\n📋 Posición en lista: #2 de 5\n📋 Oferta activa: sí\n```\n\n---\n\n## Ejemplo completo de escalamiento\n\n**Caso:** Contacto en posición #2 de la lista de espera de Spinning Matutino solicita acceso prioritario por condición médica.\n\n**Registro de escalamiento generado:**\n\n```json\n{\n  \"escalamiento_id\": \"WL-ESC-20260823110000\",\n  \"agente\": \"waitlist-class-filler\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"contacto_id\": \"CT-2026-00601\",\n  \"contacto_nombre\": \"Pedro Sánchez\",\n  \"clase_id\": \"CLS-2026-0823-0900-SPIN\",\n  \"clase_nombre\": \"Spinning Matutino\",\n  \"motivo_escalamiento\": \"solicitud_excepcion_especial\",\n  \"descripcion_caso\": \"El contacto solicita acceso prioritario por condición médica que requiere adaptación del equipamiento. Necesita evaluar si la clase es apta para su condición antes de confirmar.\",\n  \"urgencia\": \"media\",\n  \"asignado_a\": \"Carlos Pérez (Instructor responsable)\",\n  \"timestamp_escalamiento\": \"2026-08-23T11:00:00Z\",\n  \"datos_contexto\": {\n    \"lista_espera_posicion\": 2,\n    \"total_en_lista\": 5,\n    \"oferta_activa\": true\n  }\n}\n```\n\n**Acciones tomadas:**\n1. Se envió mensaje al contacto indicando que su caso requiere atención personalizada.\n2. Se notificó al instructor responsable Carlos Pérez por WhatsApp y email.\n3. Se pausó la oferta de espacio al contacto hasta recibir instrucción del humano.\n4. El espacio liberado sigue disponible para ofrecer al siguiente contacto si el humano autoriza.\n\n---\n*Plantilla generada para el nicho Gimnasios y Fitness — Agente Waitlist Class Filler*\n"
              },
              {
                "id": "waitlist-class-filler/variables-entrada.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "variables-entrada.md",
                "contenido": "# Variables de entrada — Waitlist Class Filler\n\n## Descripción\n\nLista fija de los campos de datos que el agente necesita recibir para operar. Todos los campos deben estar presentes en el evento o consulta que dispara el agente.\n\n---\n\n## Variables de entrada\n\n### Datos del evento\n\n| # | Nombre del campo | Tipo de dato | Obligatorio/Opcional | Descripción | Ejemplo |\n|---|------------------|--------------|----------------------|-------------|---------|\n| 1 | `evento_tipo` | string (enum) | Obligatorio | Tipo de evento que dispara el agente | `\"espacio_liberado\"`, `\"solicitud_lista_espera\"`, `\"consulta_posicion\"`, `\"retiro_lista\"` |\n| 2 | `evento_fecha_hora` | datetime (ISO 8601) | Obligatorio | Fecha y hora del evento | `\"2026-08-23T10:30:00Z\"` |\n| 3 | `evento_origen` | string (enum) | Obligatorio | Canal por el que se recibe el evento | `\"whatsapp\"`, `\"telefono\"`, `\"email\"`, `\"app\"`, `\"sistema_calendario\"` |\n\n### Datos del contacto\n\n| # | Nombre del campo | Tipo de dato | Obligatorio/Opcional | Descripción | Ejemplo |\n|---|------------------|--------------|----------------------|-------------|---------|\n| 4 | `contacto_id` | string | Obligatorio | Identificador único del contacto | `\"CT-2026-00452\"` |\n| 5 | `contacto_nombre` | string | Obligatorio | Nombre completo del contacto | `\"María López\"` |\n| 6 | `contacto_telefono` | string | Obligatorio | Número de teléfono con código de país | `\"+54 11 5555-1234\"` |\n| 7 | `contacto_email` | string | Opcional | Correo electrónico del contacto | `\"maria.lopez@email.com\"` |\n| 8 | `contacto_canal_preferido` | string (enum) | Obligatorio | Canal preferido para comunicación | `\"whatsapp\"`, `\"sms\"`, `\"email\"`, `\"app\"` |\n| 9 | `contacto_miembro_activo` | boolean | Obligatorio | Si el contacto es miembro activo del gimnasio | `true` |\n| 10 | `contacto_plan_tipo` | string | Opcional | Tipo de plan del miembro si es activo | `\"premium\"`, `\"basico\"`, `\"estudiante\"`, `null` |\n\n### Datos de la clase o sesión\n\n| # | Nombre del campo | Tipo de dato | Obligatorio/Opcional | Descripción | Ejemplo |\n|---|------------------|--------------|----------------------|-------------|---------|\n| 11 | `clase_id` | string | Obligatorio | Identificador único de la clase | `\"CLS-2026-0823-0900-SPIN\"` |\n| 12 | `clase_nombre` | string | Obligatorio | Nombre de la clase o sesión | `\"Spinning Matutino\"` |\n| 13 | `clase_fecha` | date (YYYY-MM-DD) | Obligatorio | Fecha de la clase | `\"2026-08-24\"` |\n| 14 | `clase_hora_inicio` | time (HH:MM) | Obligatorio | Hora de inicio de la clase | `\"09:00\"` |\n| 15 | `clase_hora_fin` | time (HH:MM) | Obligatorio | Hora de fin de la clase | `\"10:00\"` |\n| 16 | `clase_instructor` | string | Obligatorio | Nombre del instructor asignado | `\"Carlos Pérez\"` |\n| 17 | `clase_aforo_maximo` | integer | Obligatorio | Capacidad máxima de la clase | `20` |\n| 18 | `clase_aforo_actual` | integer | Obligatorio | Asistentes confirmados actualmente | `18` |\n| 19 | `clase_espacios_disponibles` | integer | Obligatorio | Espacios libres disponibles (aforo_maximo - aforo_actual) | `2` |\n| 20 | `clase_sala` | string | Opcional | Sala o área donde se realiza | `\"Sala 3 - Piso 2\"` |\n\n### Datos de la lista de espera\n\n| # | Nombre del campo | Tipo de dato | Obligatorio/Opcional | Descripción | Ejemplo |\n|---|------------------|--------------|----------------------|-------------|---------|\n| 21 | `lista_espera_clase_id` | string | Obligatorio | ID de la clase para la cual se consulta la lista | `\"CLS-2026-0823-0900-SPIN\"` |\n| 22 | `lista_espera_posicion` | integer | Condicional | Posición del contacto en la lista (requerido si `evento_tipo` es `\"consulta_posicion\"`) | `3` |\n| 23 | `lista_espera_total_contactos` | integer | Obligatorio | Total de contactos en la lista de espera para esta clase | `7` |\n| 24 | `lista_espera_fecha_registro` | datetime (ISO 8601) | Condicional | Fecha y hora en que el contacto se agregó a la lista (requerido si `evento_tipo` es `\"solicitud_lista_espera\"` o `\"retiro_lista\"`) | `\"2026-08-22T14:15:00Z\"` |\n| 25 | `lista_espera_horario_preferido_1` | string | Opcional | Primera opción de horario preferida por el contacto | `\"09:00\"` |\n| 26 | `lista_espera_horario_preferido_2` | string | Opcoral | Segunda opción de horario preferida por el contacto | `\"10:00\"` |\n\n### Datos de oferta activa\n\n| # | Nombre del campo | Tipo de dato | Obligatorio/Opcional | Descripción | Ejemplo |\n|---|------------------|--------------|----------------------|-------------|---------|\n| 27 | `oferta_id` | string | Condicional | ID de la oferta activa (requerido si `evento_tipo` es `\"respuesta_oferta\"`) | `\"OF-2026-0823-001\"` |\n| 28 | `oferta_respuesta` | string (enum) | Condicional | Respuesta del contacto a la oferta (requerido si `evento_tipo` es `\"respuesta_oferta\"`) | `\"acepta\"`, `\"rechaza\"`, `\"sin_respuesta\"` |\n| 29 | `oferta_fecha_hora_envio` | datetime (ISO 8601) | Condicional | Fecha y hora en que se envió la oferta (requerido si `evento_tipo` es `\"respuesta_oferta\"`) | `\"2026-08-23T10:30:00Z\"` |\n| 30 | `oferta_plazo_horas` | integer | Obligatorio (default: 2) | Plazo en horas para responder la oferta | `2` |\n| 31 | `oferta_fecha_hora_limite` | datetime (ISO 8601) | Condicional | Fecha y hora límite para responder (requerido si `evento_tipo` es `\"respuesta_oferta\"`) | `\"2026-08-23T12:30:00Z\"` |\n\n### Datos del gimnasio\n\n| # | Nombre del campo | Tipo de dato | Obligatorio/Opcional | Descripción | Ejemplo |\n|---|------------------|--------------|----------------------|-------------|---------|\n| 32 | `gimnasio_id` | string | Obligatorio | Identificador único del gimnasio | `\"GYM-001\"` |\n| 33 | `gimnasio_nombre` | string | Obligatorio | Nombre comercial del gimnasio | `\"FitZone Centro\"` |\n| 34 | `gimnasio_telefono` | string | Obligatorio | Teléfono de contacto del gimnasio | `\"+54 11 4444-5678\"` |\n| 35 | `gimnasio_direccion` | string | Opcional | Dirección física del gimnasio | `\"Av. Principal 1234, CABA\"` |\n\n### Datos de tiempos y configuración\n\n| # | Nombre del campo | Tipo de dato | Obligatorio/Opcional | Descripción | Ejemplo |\n|---|------------------|--------------|----------------------|-------------|---------|\n| 36 | `tiempo_limite_respuesta_horas` | integer | Obligatorio (default: 2) | Horas de plazo para que el contacto responda la oferta | `2` |\n| 37 | `tiempo_maximo_ofertas_simultaneas` | integer | Obligatorio (default: 3) | Máximo de ofertas simultáneas por espacio liberado | `3` |\n| 38 | `tiempo_notificar_si_lista_supera` | integer | Obligatorio (default: 10) | Umbral para notificar al equipo si la lista supera esta cantidad | `10` |\n| 39 | `tiempo_fecha_actual` | datetime (ISO 8601) | Obligatorio | Fecha y hora actual del sistema | `\"2026-08-23T10:30:00Z\"` |\n\n---\n\n## Variables calculadas\n\nLas siguientes variables se derivan de las variables de entrada:\n\n| # | Nombre del campo | Tipo de dato | Fórmula / Lógica | Ejemplo |\n|---|------------------|--------------|------------------|---------|\n| 40 | `contacto_tiene_oferta_activa` | boolean | `true` si el contacto tiene alguna oferta con `oferta_respuesta` distinta de `\"sin_respuesta\"` y sin expirar | `false` |\n| 41 | `lista_espera_ordenada` | array | Contactos ordenados por `lista_espera_fecha_registro` de menor a mayor (orden de llegada) | `[CT-001, CT-002, CT-003]` |\n| 42 | `siguiente_contacto_disponible` | object | Primer contacto en `lista_espera_ordenada` donde `contacto_tiene_oferta_activa` es `false` | `CT-002` |\n| 43 | `oferta_expirada` | boolean | `true` si `tiempo_fecha_actual` > `oferta_fecha_hora_limite` | `false` |\n| 44 | `espacio_asignado` | boolean | `true` si `oferta_respuesta` = `\"acepta\"` y se actualizó el calendario | `true` |\n| 45 | `posicion_final_asignacion` | integer | Posición del contacto en la lista cuando fue asignado | `2` |\n\n---\n\n## Ejemplo de payload completo\n\n```json\n{\n  \"evento_tipo\": \"espacio_liberado\",\n  \"evento_fecha_hora\": \"2026-08-23T10:30:00Z\",\n  \"evento_origen\": \"sistema_calendario\",\n  \"contacto_id\": \"CT-2026-00452\",\n  \"contacto_nombre\": \"María López\",\n  \"contacto_telefono\": \"+54 11 5555-1234\",\n  \"contacto_email\": \"maria.lopez@email.com\",\n  \"contacto_canal_preferido\": \"whatsapp\",\n  \"contacto_miembro_activo\": true,\n  \"contacto_plan_tipo\": \"premium\",\n  \"clase_id\": \"CLS-2026-0823-0900-SPIN\",\n  \"clase_nombre\": \"Spinning Matutino\",\n  \"clase_fecha\": \"2026-08-24\",\n  \"clase_hora_inicio\": \"09:00\",\n  \"clase_hora_fin\": \"10:00\",\n  \"clase_instructor\": \"Carlos Pérez\",\n  \"clase_aforo_maximo\": 20,\n  \"clase_aforo_actual\": 19,\n  \"clase_espacios_disponibles\": 1,\n  \"clase_sala\": \"Sala 3 - Piso 2\",\n  \"lista_espera_clase_id\": \"CLS-2026-0823-0900-SPIN\",\n  \"lista_espera_posicion\": 1,\n  \"lista_espera_total_contactos\": 5,\n  \"lista_espera_fecha_registro\": \"2026-08-22T14:15:00Z\",\n  \"lista_espera_horario_preferido_1\": \"09:00\",\n  \"lista_espera_horario_preferido_2\": \"10:00\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"gimnasio_telefono\": \"+54 11 4444-5678\",\n  \"gimnasio_direccion\": \"Av. Principal 1234, CABA\",\n  \"tiempo_limite_respuesta_horas\": 2,\n  \"tiempo_maximo_ofertas_simultaneas\": 3,\n  \"tiempo_notificar_si_lista_supera\": 10,\n  \"tiempo_fecha_actual\": \"2026-08-23T10:30:00Z\"\n}\n```\n\n---\n*Plantilla generada para el nicho Gimnasios y Fitness — Agente Waitlist Class Filler*\n"
              }
            ]
          }
        ]
      },
      {
        "id": "review-request",
        "carpeta": "review-request-agent",
        "nombre": "Review Request Agent",
        "icono": "⭐",
        "descripcion": "Solicitar reseñas después de completar un servicio o clase",
        "submodulos": [],
        "categoria": "general",
        "estructura": [
          {
            "id": "review-request-agent",
            "etiqueta": "",
            "es_plantilla": true,
            "nombre": "review-request-agent",
            "subcarpetas": [],
            "notas": [
              {
                "id": "review-request-agent/flujo.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "flujo.md",
                "contenido": "# Flujo de ejecución — Review Request Agent\n\nEste archivo documenta el flujo fijo de ejecución del agente. Cada solicitud de reseña sigue estos 5 pasos **en orden**, sin pasos condicionales ni bifurcaciones.\n\n---\n\n## Paso 1 — Detección de que el servicio o cita fue marcado como completado\n\nEl agente detecta que una clase, sesión de entrenamiento o servicio del gimnasio fue marcado como completado en el sistema y activa el flujo de solicitud de reseña.\n\n**Acción del agente:**\n1. Recibir los datos del evento completado según lo definido en `variables-entrada.md`.\n2. Registrar la marca de tiempo de detección en `fecha_hora_deteccion_evento`.\n3. Validar que el `evento_id` tenga un formato válido.\n4. Verificar que el `estado_servicio` sea `completado`.\n5. Verificar que el evento no haya sido procesado previamente para solicitud de reseña (verificar contra el historial).\n6. Determinar la plataforma de reseñas configurada (`plataforma_resena`).\n7. Construir el `enlace_resena` completo concatenando la URL base de la plataforma con el ID del gimnasio.\n\n**Ejemplo de evento detectado (clase completada):**\n\n```json\n{\n  \"evento_id\": \"CLS-20260823-0900-SPIN-001\",\n  \"contacto_id\": \"CT-2026-00452\",\n  \"servicio_tipo\": \"clase\",\n  \"servicio_codigo\": \"SPN\",\n  \"servicio_nombre\": \"Spinning Matutino\",\n  \"fecha_evento\": \"2026-08-23\",\n  \"hora_evento\": \"09:00\",\n  \"instructor_nombre\": \"Carlos Pérez\",\n  \"estado_servicio\": \"completado\",\n  \"nombre_miembro\": \"María López\",\n  \"telefono_miembro\": \"+54 11 5555-1234\",\n  \"email_miembro\": \"maria.lopez@email.com\",\n  \"canal_solicitud\": \"whatsapp\",\n  \"plataforma_resena\": \"google\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"plataforma_id_gimnasio\": \"fitzone-centro-caba\",\n  \"notas_servicio\": \"Miembro asistió puntualmente, participó activamente\"\n}\n```\n\n**Ejemplo de evento detectado (sesión personal completada):**\n\n```json\n{\n  \"evento_id\": \"PTR-20260823-1600-FUER-002\",\n  \"contacto_id\": \"CT-2026-00389\",\n  \"servicio_tipo\": \"entrenamiento_personal\",\n  \"servicio_codigo\": \"FUE\",\n  \"servicio_nombre\": \"Sesión de Fuerza\",\n  \"fecha_evento\": \"2026-08-23\",\n  \"hora_evento\": \"16:00\",\n  \"instructor_nombre\": \"Ana Martínez\",\n  \"estado_servicio\": \"completado\",\n  \"nombre_miembro\": \"Roberto García\",\n  \"telefono_miembro\": \"+54 11 6666-7890\",\n  \"email_miembro\": null,\n  \"canal_solicitud\": \"whatsapp\",\n  \"plataforma_resena\": \"google\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"plataforma_id_gimnasio\": \"fitzone-centro-caba\",\n  \"notas_servicio\": \"Sesión de hipertrofia - tren superior\"\n}\n```\n\n---\n\n## Paso 2 — Espera del intervalo definido (ej. 2 horas después)\n\nEl agente espera el intervalo de tiempo configurado antes de enviar la solicitud de reseña.\n\n**Acción del agente:**\n1. Determinar el intervalo de espera según el tipo de servicio:\n   - Clase completada: 2 horas.\n   - Sesión de entrenamiento completada: 2 horas.\n   - Evaluación física completada: 4 horas.\n2. Calcular el `momento_envio_solicitud`: `fecha_hora_deteccion_evento` + intervalo.\n3. Verificar si el `momento_envio_solicitud` cae dentro del horario de atención (07:00–22:00):\n   - Si es antes de las 07:00 → ajustar a las 10:00 del mismo día.\n   - Si es después de las 22:00 → ajustar a las 10:00 del siguiente día hábil.\n4. Registrar el `momento_envio_solicitud_ajustado`.\n5. Esperar hasta que se alcance el momento de envío.\n6. Registrar la marca de tiempo de inicio de espera en `fecha_hora_inicio_espera`.\n\n**Tabla de intervalos de espera:**\n\n| Tipo de servicio | Intervalo | Ejemplo (servicio 23/ago 09:00) |\n|---|---|---|\n| Clase completada | 2 horas | Envío: 23/ago 11:00 → dentro de horario |\n| Sesión de entrenamiento completada | 2 horas | Envío: 23/ago 18:00 → dentro de horario |\n| Evaluación física completada | 4 horas | Envío: 23/ago 20:00 → dentro de horario |\n\n**Ejemplo de cálculo (clase completada):**\n\n```json\n{\n  \"tipo_evento\": \"clase_completada\",\n  \"intervalo_horas\": 2,\n  \"fecha_hora_deteccion_evento\": \"2026-08-23T09:00:00\",\n  \"momento_envio_solicitud_calculado\": \"2026-08-23T11:00:00\",\n  \"momento_envio_solicitud_ajustado\": \"2026-08-23T11:00:00\",\n  \"ajuste_horario\": false,\n  \"razon_ajuste\": null\n}\n```\n\n**Ejemplo de cálculo (evaluación fuera de horario):**\n\n```json\n{\n  \"tipo_evento\": \"evaluacion_fisica_completada\",\n  \"intervalo_horas\": 4,\n  \"fecha_hora_deteccion_evento\": \"2026-08-23T20:30:00\",\n  \"momento_envio_solicitud_calculado\": \"2026-08-24T00:30:00\",\n  \"momento_envio_solicitud_ajustado\": \"2026-08-24T10:00:00\",\n  \"ajuste_horario\": true,\n  \"razon_ajuste\": \"Momento calculado fuera de horario (00:30), ajustado a 10:00 del siguiente día hábil\"\n}\n```\n\n---\n\n## Paso 3 — Envío del mensaje solicitando la reseña con el enlace directo\n\nEl agente envía el mensaje de solicitud de reseña al miembro con el enlace directo a la plataforma configurada.\n\n**Acción del agente:**\n1. Generar el mensaje de solicitud usando la plantilla de `formato-salida.md` → sección 1.\n2. Insertar el `enlace_resena` completo en el mensaje.\n3. Enviar el mensaje por el `canal_solicitud`.\n4. Si el envío falla, reintentar una sola vez después de 30 segundos.\n5. Si el segundo intento falla, registrar el error y activar escalamiento (ver `reglas-escalamiento.md` → C3.1).\n6. Registrar la marca de tiempo de envío en `fecha_hora_envio_solicitud`.\n7. Actualizar el estado del caso a `solicitud_enviada`.\n8. Iniciar el contador de plazo de respuesta: **48 horas** desde el envío.\n9. Calcular el `momento_recordatorio`: `fecha_hora_envio_solicitud` + 24 horas.\n\n**Plantilla de solicitud de reseña (whatsapp/sms):**\n\n```\n¡Hola [nombre_miembro]! Gracias por asistir a [clase_nombre] con [instructor_nombre]. ¡Esperamos que la hayas pasado increíble! 💪\n\n¿Nos ayudarías con una reseña? Toma solo 1 minuto:\n🔗 [enlace_resena]\n\n¡Tu opinión nos ayuda a seguir mejorando!\n— [gimnasio_nombre]\n```\n\n**Plantilla de solicitud de reseña (email):**\n\n**Asunto:** ¿Nos dejarías una reseña? — [gimnasio_nombre]\n\n```\n¡Hola [nombre_miembro]!\n\nGracias por asistir a [clase_nombre] el [fecha_formateada]. ¡Esperamos que la hayas pasado increíble! 💪\n\n¿Nos ayudarías con una reseña? Tu opinión nos ayuda a seguir mejorando y otros miembros pueden beneficiarse de tu experiencia.\n\nDeja tu reseña aquí: [enlace_resena]\n\n¡Muchas gracias por tu confianza!\n— [gimnasio_nombre]\n```\n\n**Ejemplo renderizado (whatsapp):**\n\n```\n¡Hola María López! Gracias por asistir a Spinning Matutino con Carlos Pérez. ¡Esperamos que la hayas pasado increíble! 💪\n\n¿Nos ayudarías con una reseña? Toma solo 1 minuto:\n🔗 https://g.page/r/fitzone-centro-caba/review\n\n¡Tu opinión nos ayuda a seguir mejorando!\n— FitZone Centro\n```\n\n**Ejemplo renderizado (email):**\n\n```\n**Asunto:** ¿Nos dejarías una reseña? — FitZone Centro\n\n¡Hola María López!\n\nGracias por asistir a Spinning Matutino el 23 de agosto de 2026. ¡Esperamos que la hayas pasado increíble! 💪\n\n¿Nos ayudarías con una reseña? Tu opinión nos ayuda a seguir mejorando y otros miembros pueden beneficiarse de tu experiencia.\n\nDeja tu reseña aquí: https://g.page/r/fitzone-centro-caba/review\n\n¡Muchas gracias por tu confianza!\n— FitZone Centro\n```\n\n---\n\n## Paso 4 — Si no hay respuesta en el plazo definido, envío de un único recordatorio adicional\n\nSi el miembro no respondió en 48 horas, el agente envía un único recordatorio. No se envían más mensajes después de este.\n\n**Acción del agente:**\n1. Verificar si el plazo de 48 horas desde `fecha_hora_envio_solicitud` expiró sin respuesta.\n2. Si expiró, generar el mensaje de recordatorio usando la plantilla de `formato-salida.md` → sección 2.\n3. Verificar que el `momento_recordatorio` caiga dentro del horario de atención:\n   - Si es antes de las 07:00 → ajustar a las 10:00 del mismo día.\n   - Si es después de las 22:00 → ajustar a las 10:00 del siguiente día hábil.\n4. Enviar el recordatorio por el `canal_solicitud`.\n5. Si el envío falla, reintentar una sola vez después de 30 segundos.\n6. Si el segundo intento falla, registrar el error pero continuar (el caso se cierra como `sin_respuesta`).\n7. Registrar la marca de tiempo de envío del recordatorio en `fecha_hora_envio_recordatorio`.\n8. Actualizar el campo `recordatorio_enviado: true`.\n9. Iniciar un nuevo plazo de respuesta: **48 horas** desde el envío del recordatorio.\n10. Si el miembro responde en este segundo plazo, proceder al paso 5.\n11. Si no responde, proceder al paso 5 con resultado `sin_respuesta`.\n\n**Plantilla de recordatorio (whatsapp/sms):**\n\n```\n¡Hola [nombre_miembro]! ¿Te animas a dejar tu reseña sobre tu experiencia en [clase_nombre]?\n\n🔗 [enlace_resena]\n\nSolo toma 1 minuto. ¡Gracias! 💪\n— [gimnasio_nombre]\n```\n\n**Plantilla de recordatorio (email):**\n\n**Asunto:** Recordatorio: ¿Tu reseña? — [gimnasio_nombre]\n\n```\n¡Hola [nombre_miembro]!\n\n¿Te animas a dejar tu reseña sobre tu experiencia en [clase_nombre]?\n\nDeja tu reseña aquí: [enlace_resena]\n\nSolo toma 1 minuto y nos ayuda mucho.\n¡Gracias! 💪\n— [gimnasio_nombre]\n```\n\n**Ejemplo renderizado de recordatorio (whatsapp):**\n\n```\n¡Hola María López! ¿Te animas a dejar tu reseña sobre tu experiencia en Spinning Matutino?\n\n🔗 https://g.page/r/fitzone-centro-caba/review\n\nSolo toma 1 minuto. ¡Gracias! 💪\n— FitZone Centro\n```\n\n---\n\n## Paso 5 — Registro si la reseña fue dejada, consultando la plataforma o solicitando confirmación\n\nEl agente verifica si el miembro dejó la reseña, consultando la plataforma de reseñas o solicitando confirmación al usuario.\n\n**Acción del agente:**\n1. Esperar la respuesta del miembro dentro del plazo de 48 horas desde el último mensaje enviado (solicitud o recordatorio).\n2. Si el miembro responde con una confirmación de que dejó la reseña (ejemplo: \"Ya la dejé\", \"Listo\", \"Ya califiqué\"):\n   - Registrar como `resena_dejada_confirmada`.\n   - Cerrar caso.\n3. Si el miembro responde con \"No quiero\", \"No me interesa\", \"No voy a dejar reseña\":\n   - Registrar como `rechazada`.\n   - Cerrar caso. No insistir.\n4. Si el miembro responde con un problema o queja sobre la clase, el instructor o el servicio:\n   - Registrar como `problema_detectado`.\n   - Escalar a humano (ver `reglas-escalamiento.md` → C5.1).\n5. Si el plazo expira sin respuesta:\n   - Intentar verificar automáticamente si la reseña fue dejada en la plataforma:\n     - Si la plataforma lo permite (API de Google, Yelp, etc.), consultar si hay una reseña nueva del miembro en las últimas 72 horas.\n     - Si se detecta reseña → registrar como `resena_dejada_verificada`.\n     - Si no se detecta → registrar como `sin_resena`.\n   - Cerrar caso.\n6. Registrar la marca de tiempo de cierre en `fecha_hora_cierre`.\n7. Generar el registro de resultado final con el formato de `formato-salida.md` → sección 3.\n\n**Tabla de respuestas del miembro:**\n\n| Respuesta del miembro | Clasificación | Acción |\n|---|---|---|\n| \"Ya la dejé\", \"Listo\", \"Califiqué\", \"Listo, 5 estrellas\" | `resena_dejada_confirmada` | Cerrar caso |\n| \"No quiero\", \"No me interesa\", \"No voy a hacerlo\" | `rechazada` | Cerrar caso |\n| \"El instructor fue grosero\", \"La clase estaba sucia\", \"No funciona el aire\" | `problema_detectado` | Escalar a humano |\n| Sin respuesta en 48 horas | Verificar automáticamente | Cerrar caso |\n\n**Verificación automática de reseña (si la plataforma lo permite):**\n\n```json\n{\n  \"metodo_verificacion\": \"api_plataforma\",\n  \"plataforma\": \"google\",\n  \"resultado_verificacion\": \"resena_encontrada | sin_resena\",\n  \"fecha_resena_detectada\": \"YYYY-MM-DDTHH:MM:SS | null\",\n  \"calificacion_resena\": 0 | null,\n  \"texto_resena_detectado\": \"string | null\"\n}\n```\n\n**Ejemplo de verificación exitosa:**\n\n```json\n{\n  \"metodo_verificacion\": \"api_plataforma\",\n  \"plataforma\": \"google\",\n  \"resultado_verificacion\": \"resena_encontrada\",\n  \"fecha_resena_detectada\": \"2026-08-24T18:00:00\",\n  \"calificacion_resena\": 5,\n  \"texto_resena_detectado\": \"Excelente gimnasio, el instructor Carlos es fantástico. Las clases de spinning son lo mejor. ¡Recomendado!\"\n}\n```\n\n**Ejemplo de verificación sin reseña:**\n\n```json\n{\n  \"metodo_verificacion\": \"api_plataforma\",\n  \"plataforma\": \"google\",\n  \"resultado_verificacion\": \"sin_resena\",\n  \"fecha_resena_detectada\": null,\n  \"calificacion_resena\": null,\n  \"texto_resena_detectado\": null\n}\n```\n\n---\n*Plantilla generada para el nicho Gimnasios y Fitness — Agente Review Request Agent*\n"
              },
              {
                "id": "review-request-agent/formato-salida.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "formato-salida.md",
                "contenido": "# Formato de salida — Review Request Agent\n\n## Descripción\n\nDefine la estructura exacta del mensaje o registro que el agente debe producir en cada paso del flujo. Todos los campos deben tener valores concretos de ejemplo.\n\n---\n\n## 1. Mensaje de solicitud de reseña\n\n**Cuándo se genera:** Paso 3 — Cuando se envía la solicitud inicial de reseña.\n\n**Mensaje al miembro (WhatsApp/SMS):**\n\n```\n¡Hola [nombre_miembro]! Gracias por asistir a [clase_nombre] con [instructor_nombre]. ¡Esperamos que la hayas pasado increíble! 💪\n\n¿Nos ayudarías con una reseña? Toma solo 1 minuto:\n🔗 [enlace_resena]\n\n¡Tu opinión nos ayuda a seguir mejorando!\n— [gimnasio_nombre]\n```\n\n**Ejemplo renderizado (WhatsApp):**\n\n```\n¡Hola María López! Gracias por asistir a Spinning Matutino con Carlos Pérez. ¡Esperamos que la hayas pasado increíble! 💪\n\n¿Nos ayudarías con una reseña? Toma solo 1 minuto:\n🔗 https://g.page/r/fitzone-centro-caba/review\n\n¡Tu opinión nos ayuda a seguir mejorando!\n— FitZone Centro\n```\n\n**Mensaje por email:**\n\n**Asunto:** ¿Nos dejarías una reseña? — [gimnasio_nombre]\n\n```\n¡Hola [nombre_miembro]!\n\nGracias por asistir a [clase_nombre] el [fecha_formateada]. ¡Esperamos que la hayas pasado increíble! 💪\n\n¿Nos ayudarías con una reseña? Tu opinión nos ayuda a seguir mejorando y otros miembros pueden beneficiarse de tu experiencia.\n\nDeja tu reseña aquí: [enlace_resena]\n\n¡Muchas gracias por tu confianza!\n— [gimnasio_nombre]\n```\n\n**Ejemplo renderizado (email):**\n\n```\nAsunto: ¿Nos dejarías una reseña? — FitZone Centro\n\n¡Hola María López!\n\nGracias por asistir a Spinning Matutino el 23 de agosto de 2026. ¡Esperamos que la hayas pasado increíble! 💪\n\n¿Nos ayudarías con una reseña? Tu opinión nos ayuda a seguir mejorando y otros miembros pueden beneficiarse de tu experiencia.\n\nDeja tu reseña aquí: https://g.page/r/fitzone-centro-caba/review\n\n¡Muchas gracias por tu confianza!\n— FitZone Centro\n```\n\n---\n\n## 2. Mensaje de recordatorio\n\n**Cuándo se genera:** Paso 4 — Cuando el miembro no respondió en 48 horas y se envía el único recordatorio.\n\n**Mensaje al miembro (WhatsApp/SMS):**\n\n```\n¡Hola [nombre_miembro]! ¿Te animas a dejar tu reseña sobre tu experiencia en [clase_nombre]?\n\n🔗 [enlace_resena]\n\nSolo toma 1 minuto. ¡Gracias! 💪\n— [gimnasio_nombre]\n```\n\n**Ejemplo renderizado (WhatsApp):**\n\n```\n¡Hola María López! ¿Te animas a dejar tu reseña sobre tu experiencia en Spinning Matutino?\n\n🔗 https://g.page/r/fitzone-centro-caba/review\n\nSolo toma 1 minuto. ¡Gracias! 💪\n— FitZone Centro\n```\n\n**Mensaje por email:**\n\n**Asunto:** Recordatorio: ¿Tu reseña? — [gimnasio_nombre]\n\n```\n¡Hola [nombre_miembro]!\n\n¿Te animas a dejar tu reseña sobre tu experiencia en [clase_nombre]?\n\nDeja tu reseña aquí: [enlace_resena]\n\nSolo toma 1 minuto y nos ayuda mucho.\n¡Gracias! 💪\n— [gimnasio_nombre]\n```\n\n**Ejemplo renderizado (email):**\n\n```\nAsunto: Recordatorio: ¿Tu reseña? — FitZone Centro\n\n¡Hola María López!\n\n¿Te animas a dejar tu reseña sobre tu experiencia en Spinning Matutino?\n\nDeja tu reseña aquí: https://g.page/r/fitzone-centro-caba/review\n\nSolo toma 1 minuto y nos ayuda mucho.\n¡Gracias! 💪\n— FitZone Centro\n```\n\n---\n\n## 3. Registro de resultado final\n\n**Cuándo se genera:** Paso 5 — Cuando se cierra el caso con el resultado definitivo.\n\n**Estructura:**\n\n```json\n{\n  \"registro_id\": \"RRA-[timestamp]\",\n  \"agente\": \"review-request-agent\",\n  \"gimnasio_id\": \"[gimnasio_id]\",\n  \"gimnasio_nombre\": \"[gimnasio_nombre]\",\n  \"contacto_id\": \"[contacto_id]\",\n  \"nombre_miembro\": \"[nombre_miembro]\",\n  \"evento_id\": \"[evento_id]\",\n  \"servicio_tipo\": \"[servicio_tipo]\",\n  \"servicio_nombre\": \"[servicio_nombre]\",\n  \"instructor_nombre\": \"[instructor_nombre]\",\n  \"fecha_evento\": \"[fecha_evento]\",\n  \"plataforma_resena\": \"[plataforma_resena]\",\n  \"enlace_resena\": \"[enlace_resena]\",\n  \"canal_solicitud\": \"[canal_solicitud]\",\n  \"fecha_hora_deteccion_evento\": \"[fecha_hora_deteccion_evento]\",\n  \"intervalo_horas\": \"[intervalo_horas]\",\n  \"momento_envio_solicitud_ajustado\": \"[momento_envio_solicitud_ajustado]\",\n  \"fecha_hora_envio_solicitud\": \"[fecha_hora_envio_solicitud]\",\n  \"recordatorio_enviado\": \"[recordatorio_enviado]\",\n  \"fecha_hora_envio_recordatorio\": \"[fecha_hora_envio_recordatorio]\",\n  \"respuesta_miembro\": \"[respuesta_miembro]\",\n  \"texto_respuesta\": \"[texto_respuesta]\",\n  \"fecha_hora_respuesta\": \"[fecha_hora_respuesta]\",\n  \"verificacion_resena\": \"[verificacion_resena]\",\n  \"resultado\": \"[resultado]\",\n  \"fecha_hora_cierre\": \"[fecha_hora_cierre]\",\n  \"duracion_proceso_horas\": \"[duracion_proceso_horas]\"\n}\n```\n\n**Ejemplo de registro completo (reseña dejada confirmada):**\n\n```json\n{\n  \"registro_id\": \"RRA-20260824160000\",\n  \"agente\": \"review-request-agent\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"contacto_id\": \"CT-2026-00452\",\n  \"nombre_miembro\": \"María López\",\n  \"evento_id\": \"CLS-20260823-0900-SPIN-001\",\n  \"servicio_tipo\": \"clase\",\n  \"servicio_nombre\": \"Spinning Matutino\",\n  \"instructor_nombre\": \"Carlos Pérez\",\n  \"fecha_evento\": \"2026-08-23\",\n  \"plataforma_resena\": \"google\",\n  \"enlace_resena\": \"https://g.page/r/fitzone-centro-caba/review\",\n  \"canal_solicitud\": \"whatsapp\",\n  \"fecha_hora_deteccion_evento\": \"2026-08-23T09:00:00\",\n  \"intervalo_horas\": 2,\n  \"momento_envio_solicitud_ajustado\": \"2026-08-23T11:00:00\",\n  \"fecha_hora_envio_solicitud\": \"2026-08-23T11:00:00\",\n  \"recordatorio_enviado\": false,\n  \"fecha_hora_envio_recordatorio\": null,\n  \"respuesta_miembro\": \"resena_dejada_confirmada\",\n  \"texto_respuesta\": \"Ya la dejé, 5 estrellas\",\n  \"fecha_hora_respuesta\": \"2026-08-23T15:30:00\",\n  \"verificacion_resena\": null,\n  \"resultado\": \"resena_dejada_confirmada\",\n  \"fecha_hora_cierre\": \"2026-08-23T15:30:00\",\n  \"duracion_proceso_horas\": 6.5\n}\n```\n\n**Ejemplo de registro completo (reseña verificada automáticamente):**\n\n```json\n{\n  \"registro_id\": \"RRA-20260825100000\",\n  \"agente\": \"review-request-agent\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"contacto_id\": \"CT-2026-00389\",\n  \"nombre_miembro\": \"Roberto García\",\n  \"evento_id\": \"PTR-20260823-1600-FUER-002\",\n  \"servicio_tipo\": \"entrenamiento_personal\",\n  \"servicio_nombre\": \"Sesión de Fuerza\",\n  \"instructor_nombre\": \"Ana Martínez\",\n  \"fecha_evento\": \"2026-08-23\",\n  \"plataforma_resena\": \"google\",\n  \"enlace_resena\": \"https://g.page/r/fitzone-centro-caba/review\",\n  \"canal_solicitud\": \"whatsapp\",\n  \"fecha_hora_deteccion_evento\": \"2026-08-23T16:00:00\",\n  \"intervalo_horas\": 2,\n  \"momento_envio_solicitud_ajustado\": \"2026-08-23T18:00:00\",\n  \"fecha_hora_envio_solicitud\": \"2026-08-23T18:00:00\",\n  \"recordatorio_enviado\": true,\n  \"fecha_hora_envio_recordatorio\": \"2026-08-24T18:00:00\",\n  \"respuesta_miembro\": \"sin_respuesta\",\n  \"texto_respuesta\": null,\n  \"fecha_hora_respuesta\": null,\n  \"verificacion_resena\": {\n    \"metodo_verificacion\": \"api_plataforma\",\n    \"plataforma\": \"google\",\n    \"resultado_verificacion\": \"resena_encontrada\",\n    \"fecha_resena_detectada\": \"2026-08-25T09:15:00\",\n    \"calificacion_resena\": 5,\n    \"texto_resena_detectado\": \"Excelente entrenador, Ana sabe mucho de fuerza. Las sesiones son intensas pero muy bien estructuradas.\"\n  },\n  \"resultado\": \"resena_dejada_verificada\",\n  \"fecha_hora_cierre\": \"2026-08-25T10:00:00\",\n  \"duracion_proceso_horas\": 42.0\n}\n```\n\n**Ejemplo de registro completo (sin reseña):**\n\n```json\n{\n  \"registro_id\": \"RRA-20260825100001\",\n  \"agente\": \"review-request-agent\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"contacto_id\": \"CT-2026-00501\",\n  \"nombre_miembro\": \"Lucas Fernández\",\n  \"evento_id\": \"CLS-20260823-1800-YGA-003\",\n  \"servicio_tipo\": \"clase\",\n  \"servicio_nombre\": \"Yoga Relajación\",\n  \"instructor_nombre\": \"Laura Gómez\",\n  \"fecha_evento\": \"2026-08-23\",\n  \"plataforma_resena\": \"google\",\n  \"enlace_resena\": \"https://g.page/r/fitzone-centro-caba/review\",\n  \"canal_solicitud\": \"whatsapp\",\n  \"fecha_hora_deteccion_evento\": \"2026-08-23T18:00:00\",\n  \"intervalo_horas\": 2,\n  \"momento_envio_solicitud_ajustado\": \"2026-08-23T20:00:00\",\n  \"fecha_hora_envio_solicitud\": \"2026-08-23T20:00:00\",\n  \"recordatorio_enviado\": true,\n  \"fecha_hora_envio_recordatorio\": \"2026-08-24T20:00:00\",\n  \"respuesta_miembro\": \"sin_respuesta\",\n  \"texto_respuesta\": null,\n  \"fecha_hora_respuesta\": null,\n  \"verificacion_resena\": {\n    \"metodo_verificacion\": \"api_plataforma\",\n    \"plataforma\": \"google\",\n    \"resultado_verificacion\": \"sin_resena\",\n    \"fecha_resena_detectada\": null,\n    \"calificacion_resena\": null,\n    \"texto_resena_detectado\": null\n  },\n  \"resultado\": \"sin_resena\",\n  \"fecha_hora_cierre\": \"2026-08-25T10:00:00\",\n  \"duracion_proceso_horas\": 40.0\n}\n```\n\n---\n\n## 4. Registro de escalamiento\n\n**Cuándo se genera:** Cuando el agente escala el caso a un humano.\n\n**Estructura:**\n\n```json\n{\n  \"escalamiento_id\": \"RRA-ESC-[timestamp]\",\n  \"agente\": \"review-request-agent\",\n  \"gimnasio_id\": \"[gimnasio_id]\",\n  \"gimnasio_nombre\": \"[gimnasio_nombre]\",\n  \"contacto_id\": \"[contacto_id]\",\n  \"nombre_miembro\": \"[nombre_miembro]\",\n  \"evento_id\": \"[evento_id]\",\n  \"servicio_nombre\": \"[servicio_nombre]\",\n  \"motivo_escalamiento\": \"[motivo_enum]\",\n  \"descripcion_caso\": \"[texto_descriptivo]\",\n  \"urgencia\": \"[alta | media | baja]\",\n  \"asignado_a\": \"[nombre_humano_asignado]\",\n  \"timestamp_escalamiento\": \"[tiempo_fecha_actual]\",\n  \"datos_contexto\": {\n    \"fecha_evento\": \"[fecha_evento]\",\n    \"instructor_nombre\": \"[instructor_nombre]\",\n    \"plataforma_resena\": \"[plataforma_resena]\",\n    \"resultado_hasta_momento\": \"[resultado_enum]\"\n  }\n}\n```\n\n**Ejemplo de registro de escalamiento:**\n\n```json\n{\n  \"escalamiento_id\": \"RRA-ESC-20260823120000\",\n  \"agente\": \"review-request-agent\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"contacto_id\": \"CT-2026-00601\",\n  \"nombre_miembro\": \"Pedro Sánchez\",\n  \"evento_id\": \"CLS-20260823-0900-SPIN-001\",\n  \"servicio_nombre\": \"Spinning Matutino\",\n  \"motivo_escalamiento\": \"problema_detectado\",\n  \"descripcion_caso\": \"El miembro reportó que el instructor Carlos Pérez fue grosero durante la clase y lo humilló frente a otros asistentes. Solicita hablar con un responsable.\",\n  \"urgencia\": \"alta\",\n  \"asignado_a\": \"Gerencia FitZone Centro\",\n  \"timestamp_escalamiento\": \"2026-08-23T12:00:00\",\n  \"datos_contexto\": {\n    \"fecha_evento\": \"2026-08-23\",\n    \"instructor_nombre\": \"Carlos Pérez\",\n    \"plataforma_resena\": \"google\",\n    \"resultado_hasta_momento\": \"problema_detectado\"\n  }\n}\n```\n\n---\n\n## 5. Formato de fechas\n\n| Campo | Formato | Ejemplo |\n|---|---|---|\n| Fecha del evento | `DD de mes de YYYY` | `\"23 de agosto de 2026\"` |\n| Fecha de envío | `DD/MM/YYYY HH:MM` | `\"23/08/2026 11:00\"` |\n| Fecha ISO 8601 | `YYYY-MM-DDTHH:MM:SS` | `\"2026-08-23T11:00:00\"` |\n| Hora | `HH:MM` | `\"11:00\"` |\n\n---\n*Plantilla generada para el nicho Gimnasios y Fitness — Agente Review Request Agent*\n"
              },
              {
                "id": "review-request-agent/prompt-sistema.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "prompt-sistema.md",
                "contenido": "# Prompt del Sistema — Review Request Agent\n\n## Identidad\nEres **Review Request Agent**, un asistente automatizado de **[Nombre del Gimnasio]** especializado en solicitar reseñas a clientes después de completar un servicio o clase. Tu función es ejecutar el flujo de 5 pasos exactos que se define a continuación.\n\n## Tono de comunicación\n- Profesional y cercano\n- Agradecido por la confianza del cliente\n- Sin ser insistente ni agresivo\n- Orientado a facilitar el proceso de reseña\n\n## Flujo de ejecución\n\nEl agente ejecuta estos 5 pasos en orden, sin bifurcaciones ni condicionales:\n\n### Paso 1: Detecta que el servicio o cita fue marcado como completado\nEl agente detecta cuando un servicio o cita es registrado como completado en el sistema.\n\n### Paso 2: Espera el intervalo definido\nEl agente espera 2 horas después de la marca de completado antes de continuar.\n\n### Paso 3: Envía mensaje solicitando la reseña\nEl agente envía un mensaje al cliente con el enlace directo a la plataforma de reseñas configurada.\n\n### Paso 4: Envía recordatorio si no hay respuesta\nSi no hay respuesta en 48 horas, el agente envía un único recordatorio adicional.\n\n### Paso 5: Registra si la reseña fue dejada\nEl agente consulta la plataforma o solicita confirmación al usuario para registrar el resultado.\n\n## Ejemplo de ejecución\n\n**Entrada:**\n```\nevento_disparador: \"servicio_completado\"\ncliente_id: \"CLIENT-8821\"\nnombre_cliente: \"Carlos Méndez\"\nservicio_realizado: \"Clase de CrossFit\"\nplataforma_resenas: \"Google\"\nenlace_resena: \"https://g.page/review-xyz\"\n```\n\n**Ejecución paso a paso:**\n\n**Paso 1:** Detecta servicio completado: \"Clase de CrossFit\" para Carlos Méndez (CLIENT-8821)\n\n**Paso 2:** Espera 2 horas desde la marca de completado (2026-08-23 10:00 → 12:00)\n\n**Paso 3:** Envía mensaje a Carlos:\n```\nHola Carlos, ¡gracias por asistir a tu clase de CrossFit! 🏋️\nTu opinión nos ayuda a mejorar. ¿Podrías dejarnos una reseña en Google?\nEnlace directo: https://g.page/review-xyz\n¡Gracias por tu tiempo!\n```\n\n**Paso 4:** Si en 48 horas no hay respuesta, envía recordatorio:\n```\nHola Carlos, ¿pudiste dejarnos tu reseña? 🙏\nTu opinión es muy valiosa para nosotros: https://g.page/review-xyz\n```\n\n**Paso 5:** Registra resultado: \"reseña_enviada\" → \"pendiente_respuesta\" → \"confirmada\" o \"sin_respuesta\"\n\n## Plataformas de reseñas soportadas\n\n| Plataforma | Enlace directo | Formato |\n|------------|----------------|---------|\n| Google | `https://g.page/[slug]/review` | URL corta |\n| Facebook | `https://facebook.com/[page]/reviews` | URL estándar |\n| TripAdvisor | `https://tripadvisor.com/Attraction_Review-[id]` | URL estándar |\n| Yelp | `https://yelp.com/biz/[slug]` | URL estándar |\n\n## Restricciones\n1. Ejecutar solo los 5 pasos del flujo, sin agregar pasos adicionales\n2. No enviar más de 1 recordatorio por servicio completado\n3. Esperar el intervalo de 2 horas antes del primer mensaje\n4. Esperar 48 horas antes del recordatorio\n5. Usar únicamente el enlace de reseña proporcionado en la entrada\n6. No modificar el nombre del cliente ni los datos del servicio\n\n## Reglas de escalamiento\nEl agente escala a un humano en las siguientes situaciones:\n\n| Condición | Tiempo de respuesta |\n|-----------|---------------------|\n| Plataforma de reseñas no responde o está caída | 1 hora |\n| Cliente solicita eliminar o modificar su reseña | 15 minutos |\n| Reseña contiene contenido ofensivo o falso | 15 minutos |\n| Error en el enlace de reseña (URL no válida) | 30 minutos |\n| Cliente reporta problema técnico con la plataforma | 30 minutos |\n| Más de 3 reseñas negativas en 7 días | 1 hora |\n"
              },
              {
                "id": "review-request-agent/reglas-escalamiento.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "reglas-escalamiento.md",
                "contenido": "# Reglas de escalamiento — Review Request Agent\n\n## Descripción\n\nLista las condiciones exactas bajo las cuales el agente debe transferir el caso a un humano, sin dejar casos ambiguos. Cada condición tiene un identificador único, la situación que la activa, la acción a tomar y el plazo de respuesta.\n\n---\n\n## Condiciones de escalamiento\n\n### Categoría 1: Problemas con el servicio reportados por el miembro\n\n| ID | Condición | Acción del agente | Urgencia | Plazo de respuesta |\n|----|-----------|-------------------|----------|-------------------|\n| C1.1 | El miembro reporta un problema con la clase (limpieza, equipamiento, temperatura, ruido) | Escalar a [gerencia/encargado de operaciones] con los detalles del problema. No continuar con la solicitud de reseña. | Alta | 4 horas |\n| C1.2 | El miembro reporta un problema con el instructor (comportamiento, puntualidad, trato) | Escalar a [gerencia] con los detalles del reporte. No continuar con la solicitud de reseña. | Alta | 2 horas |\n| C1.3 | El miembro reporta una lesión o incidente durante la clase | Escalar a [gerencia + seguro si aplica] inmediatamente. No continuar con la solicitud de reseña. | Alta | 1 hora |\n| C1.4 | El miembro menciona que el instructor faltó o la clase fue cancelada sin aviso | Escalar a [gerencia de operaciones] con los detalles. No continuar con la solicitud de reseña. | Alta | 4 horas |\n\n### Categoría 2: Problemas técnicos de envío\n\n| ID | Condición | Acción del agente | Urgencia | Plazo de respuesta |\n|----|-----------|-------------------|----------|-------------------|\n| C2.1 | El envío del mensaje falla dos veces consecutivas | Registrar el error y escalar a [equipo técnico]. El caso queda pendiente hasta resolución técnica. | Media | 24 horas |\n| C2.2 | El canal de comunicación no está configurado o es inválido | Escalar a [equipo técnico] para verificar la configuración del canal. No enviar mensajes hasta resolver. | Media | 24 horas |\n| C2.3 | El enlace de reseña generado es inválido o devuelve error 404 | Escalar a [equipo técnico] para verificar la URL de la plataforma. No enviar mensajes con enlace roto. | Media | 4 horas |\n\n### Categoría 3: Datos del miembro incompletos o inválidos\n\n| ID | Condición | Acción del agente | Urgencia | Plazo de respuesta |\n|----|-----------|-------------------|----------|-------------------|\n| C3.1 | El `telefono_miembro` no tiene formato válido (menos de 8 o más de 15 dígitos) | Escalar a [equipo de datos] para verificar el registro del miembro. No enviar mensajes por SMS/WhatsApp hasta corregir. | Baja | 24 horas |\n| C3.2 | El `email_miembro` no tiene formato válido y el canal es `email` | Escalar a [equipo de datos] para verificar el registro. Cambiar a WhatsApp si el teléfono es válido, o escalar. | Baja | 24 horas |\n| C3.3 | El miembro no existe en la base de datos del CRM | Escalar a [equipo de datos] para verificar. No continuar con la solicitud de reseña. | Baja | 24 horas |\n\n### Categoría 4: Solicitudes especiales del miembro\n\n| ID | Condición | Acción del agente | Urgencia | Plazo de respuesta |\n|----|-----------|-------------------|----------|-------------------|\n| C4.1 | El miembro solicita que no se le envíen mensajes de ningún tipo | Registrar la preferencia y cerrar caso. No insistir. Notificar a [equipo de datos] para actualizar el perfil. | Baja | 48 horas |\n| C4.2 | El miembro solicita hablar con un humano específico (entrenador, gerente) | Escalar a la persona solicitada si es identificable. Si no, escalar a [gerencia]. | Media | 4 horas |\n| C4.3 | El miembro es patrocinador, influencer o tiene acuerdo comercial | Escalar a [gerencia] para verificar el acuerdo y dar instrucciones de manejo especial. | Media | 4 horas |\n\n### Categoría 5: Problemas con la plataforma de reseñas\n\n| ID | Condición | Acción del agente | Urgencia | Plazo de respuesta |\n|----|-----------|-------------------|----------|-------------------|\n| C5.1 | La verificación automática de reseñas falla (API no disponible, error de autenticación) | Registrar el intento fallido. El caso se cierra como `sin_verificacion` y se espera confirmación manual del miembro. No escalar a menos que falle 3 veces consecutivas. | Baja | 48 horas |\n| C5.2 | La plataforma de reseñas cambia su URL y los enlaces generados dejan de funcionar | Escalar a [equipo técnico] para actualizar la configuración de URLs. Pausar el agente hasta resolver. | Alta | 4 horas |\n| C5.3 | El gimnasio recibe una reseña negativa en la misma ventana de tiempo del caso | No escalar automáticamente. Solo informar a [gerencia] si la reseña negativa está vinculada al mismo miembro. | Baja | 48 horas |\n\n### Categoría 6: Errores del sistema o datos inconsistentes\n\n| ID | Condición | Acción del agente | Urgencia | Plazo de respuesta |\n|----|-----------|-------------------|----------|-------------------|\n| C6.1 | El evento fue procesado previamente (duplicado) | No reenviar la solicitud. Registrar como duplicado y cerrar caso. | Baja | No requiere |\n| C6.2 | El `estado_servicio` no es `completado` (ej. `cancelado`, `no_asistió`) | No procesar solicitud de reseña. Registrar y cerrar caso. | Baja | No requiere |\n| C6.3 | El intervalo de tiempo calculado genera un envío más de 7 días después del evento | No procesar. Registrar como `evento_demasiado_antiguo` y cerrar caso. | Baja | No requiere |\n\n---\n\n## Reglas de asignación de urgencia\n\n| Nivel | Color | Significado | Tiempo máximo de espera |\n|-------|-------|-------------|------------------------|\n| **Alta** | 🔴 | Requiere intervención humana inmediata para resolver un problema que afecta al miembro o al negocio | 4 horas |\n| **Media** | 🟡 | Requiere revisión humana para resolver la situación correctamente | 24 horas |\n| **Baja** | 🟢 | Informativo, requiere seguimiento pero no bloquea el flujo | 48 horas |\n\n---\n\n## Reglas de notificación por urgencia\n\n| Urgencia | Canal de notificación | Destinatario |\n|----------|----------------------|--------------|\n| **Alta** | WhatsApp + Email | [Responsable directo] + [Supervisor] |\n| **Media** | Email | [Responsable directo] |\n| **Baja** | Registro en sistema | [Equipo de operaciones] |\n\n---\n\n## Textos base para escalamiento\n\n### Al miembro (cuando se escala):\n\n```\nGracias por tu mensaje, [nombre_miembro]. Tu caso requiere atención personalizada de nuestro equipo. Te contactaremos en un máximo de [plazo] horas.\n\nMientras tanto, tu solicitud de reseña queda en pausa.\n— [gimnasio_nombre]\n```\n\n**Ejemplo:**\n\n```\nGracias por tu mensaje, Pedro Sánchez. Tu caso requiere atención personalizada de nuestro equipo. Te contactaremos en un máximo de 4 horas.\n\nMientras tanto, tu solicitud de reseña queda en pausa.\n— FitZone Centro\n```\n\n### Al equipo interno (notificación de escalamiento):\n\n```\n🚨 ESCALAMIENTO — Review Request Agent\n\n📋 Caso: [escalamiento_id]\n👤 Miembro: [nombre_miembro] ([contacto_id])\n📞 [telefono_miembro]\n🏋️ Clase: [servicio_nombre] ([evento_id])\n📅 [fecha_evento] | [instructor_nombre]\n\n⚠️ Motivo: [motivo_escalamiento]\n📝 Detalle: [descripcion_caso]\n🔴 Urgencia: [urgencia]\n⏰ Plazo de respuesta: [plazo] horas\n👤 Asignado a: [asignado_a]\n```\n\n**Ejemplo:**\n\n```\n🚨 ESCALAMIENTO — Review Request Agent\n\n📋 Caso: RRA-ESC-20260823120000\n👤 Miembro: Pedro Sánchez (CT-2026-00601)\n📞 +54 11 6666-7890\n🏋️ Clase: Spinning Matutino (CLS-20260823-0900-SPIN-001)\n📅 2026-08-23 | Carlos Pérez\n\n⚠️ Motivo: problema_detectado\n📝 Detalle: El miembro reportó que el instructor Carlos Pérez fue grosero durante la clase y lo humilló frente a otros asistentes. Solicita hablar con un responsable.\n🔴 Urgencia: alta\n⏰ Plazo de respuesta: 2 horas\n👤 Asignado a: Gerencia FitZone Centro\n```\n\n---\n\n## Ejemplo completo de escalamiento\n\n**Caso:** Miembro reporta problema con el instructor después de recibir solicitud de reseña.\n\n**Registro de escalamiento generado:**\n\n```json\n{\n  \"escalamiento_id\": \"RRA-ESC-20260823120000\",\n  \"agente\": \"review-request-agent\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"contacto_id\": \"CT-2026-00601\",\n  \"nombre_miembro\": \"Pedro Sánchez\",\n  \"evento_id\": \"CLS-20260823-0900-SPIN-001\",\n  \"servicio_nombre\": \"Spinning Matutino\",\n  \"motivo_escalamiento\": \"problema_detectado\",\n  \"descripcion_caso\": \"El miembro reportó que el instructor Carlos Pérez fue grosero durante la clase y lo humilló frente a otros asistentes. Solicita hablar con un responsable.\",\n  \"urgencia\": \"alta\",\n  \"asignado_a\": \"Gerencia FitZone Centro\",\n  \"timestamp_escalamiento\": \"2026-08-23T12:00:00\",\n  \"datos_contexto\": {\n    \"fecha_evento\": \"2026-08-23\",\n    \"instructor_nombre\": \"Carlos Pérez\",\n    \"plataforma_resena\": \"google\",\n    \"resultado_hasta_momento\": \"problema_detectado\"\n  }\n}\n```\n\n**Acciones tomadas:**\n1. Se envió mensaje al miembro indicando que su caso requiere atención personalizada.\n2. Se notificó a Gerencia FitZone Centro por WhatsApp y email.\n3. Se pausó la solicitud de reseña hasta recibir instrucción del humano.\n4. No se envió recordatorio ni se verificó reseña en la plataforma.\n\n---\n*Plantilla generada para el nicho Gimnasios y Fitness — Agente Review Request Agent*\n"
              },
              {
                "id": "review-request-agent/variables-entrada.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "variables-entrada.md",
                "contenido": "# Variables de entrada — Review Request Agent\n\nEste archivo define la lista fija y cerrada de campos de datos que el agente necesita recibir para operar. No se admiten campos adicionales a los listados aquí.\n\n---\n\n## Campos del evento disparador\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 1 | `evento_id` | `string` | Obligatorio | Identificador único del evento. Formato: `CLS-YYYYMMDD-HHMM-CODIGO-XXX` (clase), `PTR-YYYYMMDD-HHMM-CODIGO-XXX` (entrenamiento personal), `EVA-YYYYMMDD-HHMM-CODIGO-XXX` (evaluación). | `\"CLS-20260823-0900-SPIN-001\"` |\n| 2 | `contacto_id` | `string` | Obligatorio | Identificador del contacto asociado al evento. Formato: `CT-YYYY-XXXXX`. | `\"CT-2026-00452\"` |\n| 3 | `fecha_evento` | `string` (ISO 8601: `YYYY-MM-DD`) | Obligatorio | Fecha en que ocurrió el evento. | `\"2026-08-23\"` |\n| 4 | `hora_evento` | `string` (formato 24h: `HH:MM`) | Opcional | Hora en que ocurrió el evento. | `\"09:00\"` |\n| 5 | `estado_servicio` | `string` (enum) | Obligatorio | Estado del servicio. Solo se procesan servicios con estado `completado`. | `\"completado\"` |\n\n---\n\n## Datos del servicio asociado\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 6 | `servicio_tipo` | `string` (enum) | Obligatorio | Tipo de servicio realizado. Valores: `clase`, `entrenamiento_personal`, `evaluacion_fisica`, `clase_grupal`. | `\"clase\"` |\n| 7 | `servicio_codigo` | `string` (enum) | Obligatorio | Código del servicio realizado. Valores: `SPN` (Spinning), `FUE` (Fuerza), `CAR` (Cardio), `YGA` (Yoga), `PIL` (Pilates), `BOX` (Boxeo), `FUN` (Funcional), `EVA` (Evaluación), `PTF` (Personal Training Fuerza), `PTC` (Personal Training Cardio), `PTR` (Personal Training Rehabilitación). | `\"SPN\"` |\n| 8 | `servicio_nombre` | `string` | Obligatorio | Nombre legible del servicio. | `\"Spinning Matutino\"` |\n| 9 | `instructor_nombre` | `string` | Obligatorio | Nombre del instructor que dirigió la clase o sesión. | `\"Carlos Pérez\"` |\n| 10 | `sala_nombre` | `string` | Opcional | Nombre del área o sala donde se realizó la clase. | `\"Sala 3 - Piso 2\"` |\n| 11 | `notas_servicio` | `string` | Opcional | Notas o detalles del servicio realizado. | `\"Miembro asistió puntualmente, participó activamente\"` |\n\n---\n\n## Datos del contacto\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 12 | `nombre_miembro` | `string` | Obligatorio | Nombre completo del miembro. | `\"María López\"` |\n| 13 | `telefono_miembro` | `string` | Obligatorio (requerido para whatsapp/sms) | Número telefónico del miembro. Entre 8 y 15 dígitos con código de país. | `\"+54 11 5555-1234\"` |\n| 14 | `email_miembro` | `string` | Opcional (requerido si `canal_solicitud` = `email`) | Correo electrónico del miembro. | `\"maria.lopez@email.com\"` |\n| 15 | `canal_solicitud` | `string` (enum) | Opcional | Canal por el que se envía la solicitud. Valores: `whatsapp`, `sms`, `email`. Valor por defecto: `whatsapp`. | `\"whatsapp\"` |\n\n---\n\n## Datos del gimnasio (inyectados desde configuración)\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 16 | `gimnasio_id` | `string` | Obligatorio | Identificador único del gimnasio. | `\"GYM-001\"` |\n| 17 | `gimnasio_nombre` | `string` | Obligatorio | Nombre comercial del gimnasio. | `\"FitZone Centro\"` |\n| 18 | `gimnasio_telefono` | `string` | Obligatorio | Teléfono principal del gimnasio. | `\"+54 11 4444-5678\"` |\n| 19 | `gimnasio_direccion` | `string` | Opcional | Dirección física del gimnasio. | `\"Av. Principal 1234, CABA\"` |\n| 20 | `zona_horaria` | `string` | Obligatorio | Zona horaria del gimnasio en formato IANA. | `\"America/Argentina/Buenos_Aires\"` |\n| 21 | `horario_atencion_inicio` | `string` (formato 24h: `HH:MM`) | Obligatorio | Hora de inicio del horario de atención. | `\"07:00\"` |\n| 22 | `horario_atencion_fin` | `string` (formato 24h: `HH:MM`) | Obligatorio | Hora de fin del horario de atención. | `\"22:00\"` |\n\n---\n\n## Configuración de plataforma de reseñas (inyectada desde configuración)\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 23 | `plataforma_resena` | `string` (enum) | Obligatorio | Plataforma de reseñas configurada. Valores: `google`, `yelp`, `facebook`, `trustpilot`. | `\"google\"` |\n| 24 | `plataforma_url_base` | `string` | Obligatorio | URL base de la plataforma para generar el enlace de reseña. | `\"https://g.page/r/fitzone-centro-caba/review\"` |\n| 25 | `plataforma_id_gimnasio` | `string` | Obligatorio | ID o slug del gimnasio en la plataforma de reseñas. | `\"fitzone-centro-caba\"` |\n| 26 | `plataforma_api_key` | `string` | Opcional | API key para verificar reseñas automáticamente (si la plataforma lo permite). `null` si no está configurada. | `\"AIzaSy...\"` |\n\n---\n\n## Configuración de tiempos (inyectada desde configuración)\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 27 | `intervalo_solicitud_horas` | `integer` | Obligatorio | Horas de espera después del evento para enviar la solicitud. Valor por defecto: `2`. | `2` |\n| 28 | `intervalo_recordatorio_horas` | `integer` | Obligatorio | Horas de espera después de la solicitud para enviar el recordatorio. Valor por defecto: `24`. | `24` |\n| 29 | `plazo_respuesta_horas` | `integer` | Obligatorio | Horas máximas de espera para respuesta del miembro. Valor por defecto: `48`. | `48` |\n| 30 | `hora_preferida_envio` | `string` (formato 24h: `HH:MM`) | Obligatorio | Hora preferida para enviar mensajes. Valor por defecto: `\"10:00\"`. | `\"10:00\"` |\n| 31 | `verificacion_automatica` | `boolean` | Obligatorio | `true` si se intenta verificar automáticamente si la reseña fue dejada (requiere API key). `false` si solo se espera confirmación del miembro. | `true` |\n| 32 | `tiempo_fecha_actual` | `string` (ISO 8601) | Obligatorio | Fecha y hora actual del sistema. | `\"2026-08-23T10:30:00Z\"` |\n\n---\n\n## Campos calculados por el agente\n\nEstos campos **no se reciben de entrada**; el agente los genera internamente.\n\n| # | Nombre del campo | Tipo de dato | Generado en | Descripción |\n|---|---|---|---|---|\n| 33 | `registro_id` | `string` | Paso 5 | ID único del registro. Formato: `RRA-YYYYMMDD-HHMMSS-XXX`. |\n| 34 | `enlace_resena` | `string` | Paso 1 | URL completa de la plataforma de reseñas. |\n| 35 | `fecha_hora_deteccion_evento` | `string` (ISO 8601) | Paso 1 | Marca de tiempo de cuándo se detectó el evento. |\n| 36 | `intervalo_horas` | `integer` | Paso 2 | Horas de espera configuradas. |\n| 37 | `momento_envio_solicitud_calculado` | `string` (ISO 8601) | Paso 2 | Momento de envío calculado. |\n| 38 | `momento_envio_solicitud_ajustado` | `string` (ISO 8601) | Paso 2 | Momento de envío ajustado al horario. |\n| 39 | `ajuste_horario` | `boolean` | Paso 2 | `true` si se ajustó el horario. |\n| 40 | `razon_ajuste` | `string` o `null` | Paso 2 | Razón del ajuste. |\n| 41 | `fecha_hora_inicio_espera` | `string` (ISO 8601) | Paso 2 | Marca de inicio de espera. |\n| 42 | `fecha_hora_envio_solicitud` | `string` (ISO 8601) o `null` | Paso 3 | Marca de envío de la solicitud. |\n| 43 | `recordatorio_enviado` | `boolean` | Paso 4 | `true` si se envió el recordatorio. |\n| 44 | `fecha_hora_envio_recordatorio` | `string` (ISO 8601) o `null` | Paso 4 | Marca de envío del recordatorio. |\n| 45 | `respuesta_miembro` | `string` (enum) | Paso 5 | Clasificación: `resena_dejada_confirmada`, `rechazada`, `problema_detectado`, `sin_respuesta`. |\n| 46 | `texto_respuesta` | `string` o `null` | Paso 5 | Texto exacto de la respuesta. |\n| 47 | `fecha_hora_respuesta` | `string` (ISO 8601) o `null` | Paso 5 | Marca de la respuesta. |\n| 48 | `verificacion_resena` | `object` o `null` | Paso 5 | Resultado de verificación automática (si aplica). |\n| 49 | `resultado` | `string` (enum) | Paso 5 | Resultado final: `resena_dejada_confirmada`, `resena_dejada_verificada`, `rechazada`, `problema_detectado`, `sin_resena`, `sin_respuesta`. |\n| 50 | `fecha_hora_cierre` | `string` (ISO 8601) | Paso 5 | Marca de cierre del caso. |\n| 51 | `duracion_proceso_horas` | `number` | Paso 5 | Horas totales del proceso. |\n\n---\n\n## Validaciones de entrada\n\n| Campo | Regla | Acción si falla |\n|---|---|---|\n| `evento_id` | No vacío, formato válido | Terminar flujo sin ejecutar |\n| `contacto_id` | Debe seguir formato `CT-YYYY-XXXXX` | Terminar flujo sin ejecutar |\n| `estado_servicio` | Debe ser `completado` | No procesar solicitud de reseña |\n| `servicio_tipo` | Debe ser uno de los 4 valores válidos | Terminar flujo sin ejecutar |\n| `servicio_codigo` | Debe ser uno de los 11 valores válidos | Terminar flujo sin ejecutar |\n| `servicio_nombre` | No vacío | Terminar flujo sin ejecutar |\n| `instructor_nombre` | No vacío | Terminar flujo sin ejecutar |\n| `nombre_miembro` | No vacío, mínimo 2 caracteres | Terminar flujo sin ejecutar |\n| `telefono_miembro` | Entre 8 y 15 dígitos si `canal_solicitud` es `whatsapp` o `sms` | Escalar a humano (C4.1) |\n| `email_miembro` | Si `canal_solicitud` es `email`, debe ser formato válido | Escalar a humano (C4.1) |\n| `canal_solicitud` | Si está presente, debe ser `whatsapp`, `sms` o `email` | Usar `whatsapp` como valor por defecto |\n| `plataforma_resena` | Debe ser uno de los 4 valores válidos | Terminar flujo sin ejecutar |\n| `plataforma_url_base` | No vacío, formato URL válido | Terminar flujo sin ejecutar |\n| `gimnasio_id` | No vacío | Terminar flujo sin ejecutar |\n| `zona_horaria` | IANA válido | Usar `America/Argentina/Buenos_Aires` como valor por defecto |\n\n---\n\n## Ejemplo de payload completo\n\n```json\n{\n  \"evento_id\": \"CLS-20260823-0900-SPIN-001\",\n  \"contacto_id\": \"CT-2026-00452\",\n  \"fecha_evento\": \"2026-08-23\",\n  \"hora_evento\": \"09:00\",\n  \"estado_servicio\": \"completado\",\n  \"servicio_tipo\": \"clase\",\n  \"servicio_codigo\": \"SPN\",\n  \"servicio_nombre\": \"Spinning Matutino\",\n  \"instructor_nombre\": \"Carlos Pérez\",\n  \"sala_nombre\": \"Sala 3 - Piso 2\",\n  \"notas_servicio\": \"Miembro asistió puntualmente, participó activamente\",\n  \"nombre_miembro\": \"María López\",\n  \"telefono_miembro\": \"+54 11 5555-1234\",\n  \"email_miembro\": \"maria.lopez@email.com\",\n  \"canal_solicitud\": \"whatsapp\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"gimnasio_telefono\": \"+54 11 4444-5678\",\n  \"gimnasio_direccion\": \"Av. Principal 1234, CABA\",\n  \"zona_horaria\": \"America/Argentina/Buenos_Aires\",\n  \"horario_atencion_inicio\": \"07:00\",\n  \"horario_atencion_fin\": \"22:00\",\n  \"plataforma_resena\": \"google\",\n  \"plataforma_url_base\": \"https://g.page/r/fitzone-centro-caba/review\",\n  \"plataforma_id_gimnasio\": \"fitzone-centro-caba\",\n  \"plataforma_api_key\": \"AIzaSy...\",\n  \"intervalo_solicitud_horas\": 2,\n  \"intervalo_recordatorio_horas\": 24,\n  \"plazo_respuesta_horas\": 48,\n  \"hora_preferida_envio\": \"10:00\",\n  \"verificacion_automatica\": true,\n  \"tiempo_fecha_actual\": \"2026-08-23T10:30:00Z\"\n}\n```\n\n---\n*Plantilla generada para el nicho Gimnasios y Fitness — Agente Review Request Agent*\n"
              }
            ]
          }
        ]
      },
      {
        "id": "attendance-recap",
        "carpeta": "attendance-recap-agent",
        "nombre": "Attendance Recap Agent",
        "icono": "📊",
        "descripcion": "Generar reportes de asistencia y frecuencia de visitas",
        "submodulos": [],
        "categoria": "general",
        "estructura": [
          {
            "id": "attendance-recap-agent",
            "etiqueta": "",
            "es_plantilla": true,
            "nombre": "attendance-recap-agent",
            "subcarpetas": [],
            "notas": [
              {
                "id": "attendance-recap-agent/flujo.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "flujo.md",
                "contenido": "# Flujo de ejecución — Attendance Recap Agent\n\nEste archivo documenta el flujo fijo de ejecución del agente. Cada resumen de asistencia sigue estos 5 pasos **en orden**, sin pasos condicionales ni bifurcaciones.\n\n---\n\n## Paso 1 — Recepción del conjunto de datos o transcripción de entrada\n\nEl agente recibe el conjunto de datos o transcripción de entrada que contiene la actividad de asistencia del miembro. Puede provenir de check-ins, registros de clases, notas de entrenador, actividad en app o evaluaciones físicas.\n\n**Acción del agente:**\n1. Recibir los datos según lo definido en `variables-entrada.md`.\n2. Registrar la marca de tiempo de recepción en `fecha_hora_recepcion`.\n3. Validar que el `contacto_id` tenga un formato válido.\n4. Verificar que exista al menos un conjunto de datos válido (no todos los campos pueden ser `null`).\n5. Determinar el `tipo_generacion`: `semanal`, `mensual`, `solicitud_manual`, `post_evaluacion`.\n6. Determinar el `periodo_inicio` y `periodo_fin` según el tipo de generación.\n\n**Ejemplo de datos recibidos (resumen semanal):**\n\n```json\n{\n  \"contacto_id\": \"CT-2026-00452\",\n  \"nombre_miembro\": \"María López\",\n  \"tipo_generacion\": \"semanal\",\n  \"periodo_inicio\": \"2026-08-17\",\n  \"periodo_fin\": \"2026-08-23\",\n  \"datos_checkin\": [\n    {\"fecha\": \"2026-08-17\", \"hora_entrada\": \"08:15\", \"hora_salida\": \"09:45\", \"duracion_minutos\": 90, \"area\": \"Sala de spinning\"},\n    {\"fecha\": \"2026-08-18\", \"hora_entrada\": \"08:00\", \"hora_salida\": \"09:30\", \"duracion_minutos\": 90, \"area\": \"Sala de spinning\"},\n    {\"fecha\": \"2026-08-19\", \"hora_entrada\": \"17:00\", \"hora_salida\": \"18:00\", \"duracion_minutos\": 60, \"area\": \"Sala de yoga\"},\n    {\"fecha\": \"2026-08-20\", \"hora_entrada\": \"08:10\", \"hora_salida\": \"09:40\", \"duracion_minutos\": 90, \"area\": \"Sala de spinning\"},\n    {\"fecha\": \"2026-08-21\", \"hora_entrada\": \"07:30\", \"hora_salida\": \"08:30\", \"duracion_minutos\": 60, \"area\": \"Piso de gimnasio\"},\n    {\"fecha\": \"2026-08-22\", \"hora_entrada\": \"08:00\", \"hora_salida\": \"09:30\", \"duracion_minutos\": 90, \"area\": \"Sala de spinning\"},\n    {\"fecha\": \"2026-08-23\", \"hora_entrada\": \"08:05\", \"hora_salida\": \"09:35\", \"duracion_minutos\": 90, \"area\": \"Sala de spinning\"}\n  ],\n  \"datos_clases\": [\n    {\"fecha\": \"2026-08-17\", \"clase\": \"Spinning Matutino\", \"instructor\": \"Carlos Pérez\", \"sala\": \"Sala 3\", \"duracion_minutos\": 60},\n    {\"fecha\": \"2026-08-18\", \"clase\": \"Spinning Matutino\", \"instructor\": \"Carlos Pérez\", \"sala\": \"Sala 3\", \"duracion_minutos\": 60},\n    {\"fecha\": \"2026-08-19\", \"clase\": \"Yoga Relajación\", \"instructor\": \"Laura Gómez\", \"sala\": \"Sala 1\", \"duracion_minutos\": 60},\n    {\"fecha\": \"2026-08-20\", \"clase\": \"Spinning Matutino\", \"instructor\": \"Carlos Pérez\", \"sala\": \"Sala 3\", \"duracion_minutos\": 60},\n    {\"fecha\": \"2026-08-21\", \"clase\": \"Funcional Express\", \"instructor\": \"Ana Martínez\", \"sala\": \"Piso 2\", \"duracion_minutos\": 45},\n    {\"fecha\": \"2026-08-22\", \"clase\": \"Spinning Matutino\", \"instructor\": \"Carlos Pérez\", \"sala\": \"Sala 3\", \"duracion_minutos\": 60},\n    {\"fecha\": \"2026-08-23\", \"clase\": \"Spinning Matutino\", \"instructor\": \"Carlos Pérez\", \"sala\": \"Sala 3\", \"duracion_minutos\": 60}\n  ],\n  \"notas_entrenador\": [\n    {\"fecha\": \"2026-08-19\", \"instructor\": \"Laura Gómez\", \"nota\": \"María mejoró su flexibilidad en postura de paloma\"},\n    {\"fecha\": \"2026-08-23\", \"instructor\": \"Carlos Pérez\", \"nota\": \"María aumentó la resistencia, mantuvo ritmo alto todo el clase\"}\n  ],\n  \"datos_app\": {\n    \"logins_semana\": 5,\n    \"clases_consultadas\": 8,\n    \"perfil_vistas\": 2\n  },\n  \"datos_evaluacion\": null,\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"responsable_notificacion\": \"Carlos Pérez (Entrenador principal)\",\n  \"canal_notificacion\": \"email\",\n  \"email_responsable\": \"carlos.perez@fitzone.com\",\n  \"tiempo_fecha_actual\": \"2026-08-23T20:00:00Z\"\n}\n```\n\n**Ejemplo de datos recibidos (solicitud manual — miembro):**\n\n```json\n{\n  \"contacto_id\": \"CT-2026-00389\",\n  \"nombre_miembro\": \"Roberto García\",\n  \"tipo_generacion\": \"solicitud_manual\",\n  \"periodo_inicio\": \"2026-08-01\",\n  \"periodo_fin\": \"2026-08-23\",\n  \"datos_checkin\": [\n    {\"fecha\": \"2026-08-05\", \"hora_entrada\": \"16:00\", \"hora_salida\": \"17:30\", \"duracion_minutos\": 90, \"area\": \"Piso de gimnasio\"},\n    {\"fecha\": \"2026-08-08\", \"hora_entrada\": \"16:15\", \"hora_salida\": \"17:45\", \"duracion_minutos\": 90, \"area\": \"Sala de boxeo\"},\n    {\"fecha\": \"2026-08-12\", \"hora_entrada\": \"16:00\", \"hora_salida\": \"17:30\", \"duracion_minutos\": 90, \"area\": \"Piso de gimnasio\"},\n    {\"fecha\": \"2026-08-15\", \"hora_entrada\": \"16:30\", \"hora_salida\": \"18:00\", \"duracion_minutos\": 90, \"area\": \"Sala de boxeo\"},\n    {\"fecha\": \"2026-08-19\", \"hora_entrada\": \"16:00\", \"hora_salida\": \"17:30\", \"duracion_minutos\": 90, \"area\": \"Piso de gimnasio\"},\n    {\"fecha\": \"2026-08-22\", \"hora_entrada\": \"16:15\", \"hora_salida\": \"17:45\", \"duracion_minutos\": 90, \"area\": \"Sala de boxeo\"}\n  ],\n  \"datos_clases\": [\n    {\"fecha\": \"2026-08-08\", \"clase\": \"Boxeo Cardio\", \"instructor\": \"Miguel Torres\", \"sala\": \"Sala 5\", \"duracion_minutos\": 60},\n    {\"fecha\": \"2026-08-15\", \"clase\": \"Boxeo Cardio\", \"instructor\": \"Miguel Torres\", \"sala\": \"Sala 5\", \"duracion_minutos\": 60},\n    {\"fecha\": \"2026-08-22\", \"clase\": \"Boxeo Cardio\", \"instructor\": \"Miguel Torres\", \"sala\": \"Sala 5\", \"duracion_minutos\": 60}\n  ],\n  \"notas_entrenador\": [\n    {\"fecha\": \"2026-08-15\", \"instructor\": \"Miguel Torres\", \"nota\": \"Roberto mejoró su técnica de combinaciones. Pide entrenamiento de fuerza adicional.\"},\n    {\"fecha\": \"2026-08-22\", \"instructor\": \"Miguel Torres\", \"nota\": \"Roberto está listo para spar. Recomiendo sesión de evaluación de fuerza.\"}\n  ],\n  \"datos_app\": {\n    \"logins_semana\": 3,\n    \"clases_consultadas\": 5,\n    \"perfil_vistas\": 1\n  },\n  \"datos_evaluacion\": null,\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"responsable_notificacion\": \"Roberto García (Miembro)\",\n  \"canal_notificacion\": \"whatsapp\",\n  \"telefono_responsable\": \"+54 11 6666-7890\",\n  \"tiempo_fecha_actual\": \"2026-08-23T14:00:00Z\"\n}\n```\n\n---\n\n## Paso 2 — Extracción de puntos clave según lista fija de campos\n\nEl agente extrae los datos de las fuentes recibidas y los organiza en una lista fija de campos que deben completarse obligatoriamente.\n\n**Acción del agente:**\n1. Procesar cada fuente de datos recibida.\n2. Extraer valores para los siguientes campos fijos (definidos en `formato-salida.md` → sección 1):\n   - `total_clases_asistidas`\n   - `clases_por_tipo`\n   - `clase_mas_frecuente`\n   - `instructor_mas_frecuente`\n   - `horario_preferido`\n   - `duracion_promedio_sesion`\n   - `total_minutos_actividad`\n   - `racha_dias_seguidos`\n   - `primera_asistencia_periodo`\n   - `ultima_asistencia_periodo`\n   - `dias_sin_asistencia`\n   - `notas_entrenador`\n   - `actividad_app`\n   - `evaluacion_fisica_si_aplica`\n3. Marcar como `N/D` los campos que no tienen datos disponibles.\n4. Calcular campos derivados (promedio, moda, racha).\n\n**Tabla de campos extraídos:**\n\n| # | Campo | Fuente | Tipo de dato | Ejemplo |\n|---|---|---|---|---|\n| 1 | `total_clases_asistidas` | `datos_clases` | integer | `7` |\n| 2 | `clases_por_tipo` | `datos_clases` | object (mapa) | `{\"Spinning\": 5, \"Yoga\": 1, \"Funcional\": 1}` |\n| 3 | `clase_mas_frecuente` | Calculado de `clases_por_tipo` | string | `\"Spinning\"` |\n| 4 | `instructor_mas_frecuente` | `datos_clases` | string | `\"Carlos Pérez\"` |\n| 5 | `horario_preferido` | `datos_checkin` (moda de hora_entrada) | string (enum) | `\"Mañana (07:00 – 10:00)\"` |\n| 6 | `duracion_promedio_sesion` | `datos_checkin` (promedio de duracion_minutos) | integer (minutos) | `84` |\n| 7 | `total_minutos_actividad` | `datos_checkin` (suma de duracion_minutos) | integer (minutos) | `585` |\n| 8 | `racha_dias_seguidos` | `datos_checkin` (cálculo de días consecutivos) | integer | `7` |\n| 9 | `primera_asistencia_periodo` | `datos_checkin` (menor fecha) | string (DD/MM/YYYY) | `\"17/08/2026\"` |\n| 10 | `ultima_asistencia_periodo` | `datos_checkin` (mayor fecha) | string (DD/MM/YYYY) | `\"23/08/2026\"` |\n| 11 | `dias_sin_asistencia` | Calculado (días del período - días con checkin) | integer | `0` |\n| 12 | `notas_entrenador` | `notas_entrenador` | array de strings | `[\"María mejoró su flexibilidad...\", \"María aumentó la resistencia...\"]` |\n| 13 | `actividad_app` | `datos_app` | object | `{\"logins\": 5, \"clases_consultadas\": 8}` |\n| 14 | `evaluacion_fisica_si_aplica` | `datos_evaluacion` | object o `null` | `null` |\n\n**Ejemplo de extracción (María López):**\n\n```json\n{\n  \"total_clases_asistidas\": 7,\n  \"clases_por_tipo\": {\"Spinning\": 5, \"Yoga\": 1, \"Funcional\": 1},\n  \"clase_mas_frecuente\": \"Spinning\",\n  \"instructor_mas_frecuente\": \"Carlos Pérez\",\n  \"horario_preferido\": \"Mañana (07:00 – 10:00)\",\n  \"duracion_promedio_sesion\": 84,\n  \"total_minutos_actividad\": 585,\n  \"racha_dias_seguidos\": 7,\n  \"primera_asistencia_periodo\": \"17/08/2026\",\n  \"ultima_asistencia_periodo\": \"23/08/2026\",\n  \"dias_sin_asistencia\": 0,\n  \"notas_entrenador\": [\n    \"María mejoró su flexibilidad en postura de paloma (19/08 - Laura Gómez)\",\n    \"María aumentó la resistencia, mantuvo ritmo alto todo el clase (23/08 - Carlos Pérez)\"\n  ],\n  \"actividad_app\": {\"logins\": 5, \"clases_consultadas\": 8, \"perfil_vistas\": 2},\n  \"evaluacion_fisica_si_aplica\": null\n}\n```\n\n---\n\n## Paso 3 — Redacción del resumen en formato de lista con encabezados fijos\n\nEl agente genera el resumen usando la plantilla exacta definida en `formato-salida.md` → sección 1, insertando los datos extraídos en el paso 2.\n\n**Acción del agente:**\n1. Tomar la plantilla de resumen con encabezados fijos.\n2. Insertar cada campo extraído en su posición correspondiente.\n3. Formatear las cifras con separadores de miles si es necesario.\n4. Formatear las fechas en formato `DD/MM/YYYY`.\n5. Citar textualmente las notas del entrenador entre comillas.\n6. Marcar con `N/D` los campos sin datos.\n7. Generar tanto la versión de texto plano (WhatsApp/SMS) como la versión estructurada (email/registro).\n\n**Plantilla de resumen (texto plano):**\n\n```\n📋 RESUMEN DE ASISTENCIA — [nombre_miembro]\n\n📅 Período: [periodo_inicio] – [periodo_fin]\n📊 Total de clases asistidas: [total_clases_asistidas]\n🏋️ Clases más frecuentes: [clases_mas_frecuentes_formateadas]\n👤 Instructor principal: [instructor_mas_frecuente]\n⏰ Horario preferido: [horario_preferido]\n⏱️ Duración promedio: [duracion_promedio_sesion] minutos\n🔥 Racha actual: [racha_dias_seguidos] días seguidos\n📅 Primera asistencia: [primera_asistencia_periodo]\n📅 Última asistencia: [ultima_asistencia_periodo]\n🚫 Días sin asistencia: [dias_sin_asistencia]\n\n📝 Notas del entrenador:\n- \"[nota_1]\"\n- \"[nota_2]\"\n\n📱 Actividad en app:\n- Logins esta semana: [logins_semana]\n- Clases consultadas: [clases_consultadas]\n\n🏷️ Estado: [estado_miembro]\n```\n\n**Ejemplo renderizado (María López — resumen semanal):**\n\n```\n📋 RESUMEN DE ASISTENCIA — María López\n\n📅 Período: 17/08/2026 – 23/08/2026\n📊 Total de clases asistidas: 7\n🏋️ Clases más frecuentes: Spinning (5), Yoga (1), Funcional (1)\n👤 Instructor principal: Carlos Pérez\n⏰ Horario preferido: Mañana (07:00 – 10:00)\n⏱️ Duración promedio: 84 minutos\n🔥 Racha actual: 7 días seguidos\n📅 Primera asistencia: 17/08/2026\n📅 Última asistencia: 23/08/2026\n🚫 Días sin asistencia: 0\n\n📝 Notas del entrenador:\n- \"María mejoró su flexibilidad en postura de paloma\" — Laura Gómez, 19/08\n- \"María aumentó la resistencia, mantuvo ritmo alto todo el clase\" — Carlos Pérez, 23/08\n\n📱 Actividad en app:\n- Logins esta semana: 5\n- Clases consultadas: 8\n\n🏷️ Estado: Activo — Alta frecuencia\n```\n\n**Ejemplo renderizado (Roberto García — solicitud manual):**\n\n```\n📋 RESUMEN DE ASISTENCIA — Roberto García\n\n📅 Período: 01/08/2026 – 23/08/2026\n📊 Total de clases asistidas: 3 (Clases grupales) + 6 (Entrenamiento libre)\n🏋️ Clases más frecuentes: Boxeo Cardio (3)\n👤 Instructor principal: Miguel Torres\n⏰ Horario preferido: Tarde (16:00 – 18:00)\n⏱️ Duración promedio: 90 minutos\n🔥 Racha actual: 2 días seguidos (19/08 – 22/08)\n📅 Primera asistencia: 05/08/2026\n📅 Última asistencia: 22/08/2026\n🚫 Días sin asistencia: 17\n\n📝 Notas del entrenador:\n- \"Roberto mejoró su técnica de combinaciones. Pide entrenamiento de fuerza adicional.\" — Miguel Torres, 15/08\n- \"Roberto está listo para spar. Recomiendo sesión de evaluación de fuerza.\" — Miguel Torres, 22/08\n\n📱 Actividad en app:\n- Logins esta semana: 3\n- Clases consultadas: 5\n\n🏷️ Estado: Activo — Frecuencia moderada\n```\n\n---\n\n## Paso 4 — Guardado del resumen en el registro del contacto o caso\n\nEl agente guarda el resumen generado en el registro del contacto correspondiente.\n\n**Acción del agente:**\n1. Generar el `registro_id` único con formato `AR-YYYYMMDD-HHMMSS-XXX`.\n2. Almacenar el resumen completo (texto plano + datos estructurados) en el registro del contacto.\n3. Registrar la marca de tiempo de guardado en `fecha_hora_guardado`.\n4. Actualizar el campo `ultimo_resumen_fecha` en el perfil del contacto.\n5. Registrar la operación en el historial del contacto.\n\n**Estructura del registro guardado:**\n\n```json\n{\n  \"registro_id\": \"AR-[timestamp]\",\n  \"agente\": \"attendance-recap-agent\",\n  \"contacto_id\": \"[contacto_id]\",\n  \"nombre_miembro\": \"[nombre_miembro]\",\n  \"gimnasio_id\": \"[gimnasio_id]\",\n  \"tipo_generacion\": \"[tipo_generacion]\",\n  \"periodo_inicio\": \"[periodo_inicio]\",\n  \"periodo_fin\": \"[periodo_fin]\",\n  \"resumen_texto_plano\": \"[texto_del_resumen]\",\n  \"resumen_datos_estructurados\": {\n    \"total_clases_asistidas\": \"[valor]\",\n    \"clases_por_tipo\": \"[objeto]\",\n    \"clase_mas_frecuente\": \"[valor]\",\n    \"instructor_mas_frecuente\": \"[valor]\",\n    \"horario_preferido\": \"[valor]\",\n    \"duracion_promedio_sesion\": \"[valor]\",\n    \"total_minutos_actividad\": \"[valor]\",\n    \"racha_dias_seguidos\": \"[valor]\",\n    \"primera_asistencia_periodo\": \"[valor]\",\n    \"ultima_asistencia_periodo\": \"[valor]\",\n    \"dias_sin_asistencia\": \"[valor]\",\n    \"notas_entrenador\": \"[array]\",\n    \"actividad_app\": \"[objeto]\",\n    \"evaluacion_fisica_si_aplica\": \"[objeto o null]\"\n  },\n  \"estado_miembro\": \"[estado_calculado]\",\n  \"fecha_hora_generacion\": \"[tiempo_fecha_actual]\",\n  \"fecha_hora_guardado\": \"[fecha_hora_guardado]\"\n}\n```\n\n**Ejemplo de registro guardado:**\n\n```json\n{\n  \"registro_id\": \"AR-20260823200000\",\n  \"agente\": \"attendance-recap-agent\",\n  \"contacto_id\": \"CT-2026-00452\",\n  \"nombre_miembro\": \"María López\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"tipo_generacion\": \"semanal\",\n  \"periodo_inicio\": \"2026-08-17\",\n  \"periodo_fin\": \"2026-08-23\",\n  \"resumen_texto_plano\": \"📋 RESUMEN DE ASISTENCIA — María López\\n\\n📅 Período: 17/08/2026 – 23/08/2026\\n📊 Total de clases asistidas: 7\\n🏋️ Clases más frecuentes: Spinning (5), Yoga (1), Funcional (1)\\n👤 Instructor principal: Carlos Pérez\\n⏰ Horario preferido: Mañana (07:00 – 10:00)\\n⏱️ Duración promedio: 84 minutos\\n🔥 Racha actual: 7 días seguidos\\n📅 Primera asistencia: 17/08/2026\\n📅 Última asistencia: 23/08/2026\\n🚫 Días sin asistencia: 0\\n\\n📝 Notas del entrenador:\\n- \\\"María mejoró su flexibilidad en postura de paloma\\\" — Laura Gómez, 19/08\\n- \\\"María aumentó la resistencia, mantuvo ritmo alto todo el clase\\\" — Carlos Pérez, 23/08\\n\\n📱 Actividad en app:\\n- Logins esta semana: 5\\n- Clases consultadas: 8\\n\\n🏷️ Estado: Activo — Alta frecuencia\",\n  \"resumen_datos_estructurados\": {\n    \"total_clases_asistidas\": 7,\n    \"clases_por_tipo\": {\"Spinning\": 5, \"Yoga\": 1, \"Funcional\": 1},\n    \"clase_mas_frecuente\": \"Spinning\",\n    \"instructor_mas_frecuente\": \"Carlos Pérez\",\n    \"horario_preferido\": \"Mañana (07:00 – 10:00)\",\n    \"duracion_promedio_sesion\": 84,\n    \"total_minutos_actividad\": 585,\n    \"racha_dias_seguidos\": 7,\n    \"primera_asistencia_periodo\": \"17/08/2026\",\n    \"ultima_asistencia_periodo\": \"23/08/2026\",\n    \"dias_sin_asistencia\": 0,\n    \"notas_entrenador\": [\n      \"María mejoró su flexibilidad en postura de paloma — Laura Gómez, 19/08\",\n      \"María aumentó la resistencia, mantuvo ritmo alto todo el clase — Carlos Pérez, 23/08\"\n    ],\n    \"actividad_app\": {\"logins\": 5, \"clases_consultadas\": 8, \"perfil_vistas\": 2},\n    \"evaluacion_fisica_si_aplica\": null\n  },\n  \"estado_miembro\": \"Activo — Alta frecuencia\",\n  \"fecha_hora_generacion\": \"2026-08-23T20:00:00Z\",\n  \"fecha_hora_guardado\": \"2026-08-23T20:00:05Z\"\n}\n```\n\n---\n\n## Paso 5 — Notificación al responsable humano cuando el resumen esté listo\n\nEl agente notifica al responsable humano que el resumen ha sido generado y está disponible.\n\n**Acción del agente:**\n1. Determinar el canal de notificación (`canal_notificacion`).\n2. Generar el mensaje de notificación usando la plantilla de `formato-salida.md` → sección 2.\n3. Enviar la notificación al responsable.\n4. Si el envío falla, reintentar una sola vez después de 30 segundos.\n5. Si el segundo intento falla, registrar el error pero continuar (el resumen ya está guardado).\n6. Registrar la marca de tiempo de notificación en `fecha_hora_notificacion`.\n\n**Plantilla de notificación al responsable (email):**\n\n**Asunto:** Resumen de asistencia listo — [nombre_miembro] | [gimnasio_nombre]\n\n```\nHola [responsable_nombre],\n\nEl resumen de asistencia de [nombre_miembro] está listo.\n\n📋 Período: [periodo_inicio] – [periodo_fin]\n📊 Clases asistidas: [total_clases_asistidas]\n🔥 Racha: [racha_dias_seguidos] días seguidos\n\nResumen completo:\n[texto_del_resumen]\n\nRegistro guardado: [registro_id]\n— [gimnasio_nombre]\n```\n\n**Plantilla de notificación al responsable (WhatsApp/SMS):**\n\n```\n📋 Resumen de asistencia listo para [nombre_miembro]\n\n📊 Período: [periodo_inicio] – [periodo_fin]\n🏋️ Clases: [total_clases_asistidas]\n🔥 Racha: [racha_dias_seguidos] días\n\nResumen enviado por email. Registro: [registro_id]\n— [gimnasio_nombre]\n```\n\n**Plantilla de notificación al miembro (cuando él solicitó):**\n\n```\n¡Hola [nombre_miembro]! Tu resumen de asistencia está listo 📋\n\n📊 Período: [periodo_inicio] – [periodo_fin]\n🏋️ Clases asistidas: [total_clases_asistidas]\n🔥 Racha actual: [racha_dias_seguidos] días seguidos\n\nPodes ver el resumen completo en la app o pedírnoslo por este canal.\n¡Sigue así! 💪\n— [gimnasio_nombre]\n```\n\n**Ejemplo renderizado de notificación al responsable (email):**\n\n```\nAsunto: Resumen de asistencia listo — María López | FitZone Centro\n\nHola Carlos Pérez,\n\nEl resumen de asistencia de María López está listo.\n\n📋 Período: 17/08/2026 – 23/08/2026\n📊 Clases asistidas: 7\n🔥 Racha: 7 días seguidos\n\nResumen completo:\n\n📋 RESUMEN DE ASISTENCIA — María López\n\n📅 Período: 17/08/2026 – 23/08/2026\n📊 Total de clases asistidas: 7\n🏋️ Clases más frecuentes: Spinning (5), Yoga (1), Funcional (1)\n👤 Instructor principal: Carlos Pérez\n⏰ Horario preferido: Mañana (07:00 – 10:00)\n⏱️ Duración promedio: 84 minutos\n🔥 Racha actual: 7 días seguidos\n📅 Primera asistencia: 17/08/2026\n📅 Última asistencia: 23/08/2026\n🚫 Días sin asistencia: 0\n\n📝 Notas del entrenador:\n- \"María mejoró su flexibilidad en postura de paloma\" — Laura Gómez, 19/08\n- \"María aumentó la resistencia, mantuvo ritmo alto todo el clase\" — Carlos Pérez, 23/08\n\n📱 Actividad en app:\n- Logins esta semana: 5\n- Clases consultadas: 8\n\n🏷️ Estado: Activo — Alta frecuencia\n\nRegistro guardado: AR-20260823200000\n— FitZone Centro\n```\n\n**Ejemplo renderizado de notificación al miembro (WhatsApp):**\n\n```\n¡Hola María López! Tu resumen de asistencia está listo 📋\n\n📊 Período: 17/08/2026 – 23/08/2026\n🏋️ Clases asistidas: 7\n🔥 Racha actual: 7 días seguidos\n\nPodes ver el resumen completo en la app o pedírnoslo por este canal.\n¡Sigue así! 💪\n— FitZone Centro\n```\n\n---\n*Plantilla generada para el nicho Gimnasios y Fitness — Agente Attendance Recap Agent*\n"
              },
              {
                "id": "attendance-recap-agent/formato-salida.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "formato-salida.md",
                "contenido": "# Formato de salida — Attendance Recap Agent\n\n## Descripción\n\nDefine la estructura exacta del mensaje o registro que el agente debe producir en cada paso del flujo. Todos los campos deben tener valores concretos de ejemplo.\n\n---\n\n## 1. Resumen de asistencia (texto plano)\n\n**Cuándo se genera:** Paso 3 — Cuando el resumen está listo para enviar.\n\n**Plantilla con encabezados fijos:**\n\n```\n📋 RESUMEN DE ASISTENCIA — [nombre_miembro]\n\n📅 Período: [periodo_inicio_formateado] – [periodo_fin_formateado]\n📊 Total de clases asistidas: [total_clases_asistidas]\n🏋️ Clases más frecuentes: [clases_mas_frecuentes_formateadas]\n👤 Instructor principal: [instructor_mas_frecuente]\n⏰ Horario preferido: [horario_preferido]\n⏱️ Duración promedio: [duracion_promedio_sesion] minutos\n🔥 Racha actual: [racha_dias_seguidos] días seguidos\n📅 Primera asistencia: [primera_asistencia_periodo]\n📅 Última asistencia: [ultima_asistencia_periodo]\n🚫 Días sin asistencia: [dias_sin_asistencia]\n\n📝 Notas del entrenador:\n- \"[nota_1]\" — [instructor_1], [fecha_1]\n- \"[nota_2]\" — [instructor_2], [fecha_2]\n\n📱 Actividad en app:\n- Logins esta semana: [logins_semana]\n- Clases consultadas: [clases_consultadas]\n\n🏷️ Estado: [estado_miembro]\n```\n\n**Reglas de formato:**\n- Las fechas se formatean como `DD/MM/YYYY`.\n- Las notas del entrenador se citan textualmente entre comillas dobles.\n- Si un campo no tiene datos, se escribe `N/D`.\n- Si `datos_evaluacion` no es null, agregar una sección adicional:\n\n```\n📊 Evaluación física:\n- Fecha: [evaluacion_fecha]\n- Peso: [peso_kg] kg\n- IMC: [imc]\n- % Grasa: [porcentaje_grasa]%\n- Masa muscular: [masa_muscular_kg] kg\n- Fuerza: [test_fuerza]\n- Resistencia: [test_resistencia]\n- Flexibilidad: [test_flexibilidad]\n- Notas: \"[notas_evaluacion]\"\n```\n\n**Ejemplo renderizado (María López — resumen semanal, sin evaluación):**\n\n```\n📋 RESUMEN DE ASISTENCIA — María López\n\n📅 Período: 17/08/2026 – 23/08/2026\n📊 Total de clases asistidas: 7\n🏋️ Clases más frecuentes: Spinning (5), Yoga (1), Funcional (1)\n👤 Instructor principal: Carlos Pérez\n⏰ Horario preferido: Mañana (07:00 – 10:00)\n⏱️ Duración promedio: 84 minutos\n🔥 Racha actual: 7 días seguidos\n📅 Primera asistencia: 17/08/2026\n📅 Última asistencia: 23/08/2026\n🚫 Días sin asistencia: 0\n\n📝 Notas del entrenador:\n- \"María mejoró su flexibilidad en postura de paloma\" — Laura Gómez, 19/08\n- \"María aumentó la resistencia, mantuvo ritmo alto todo el clase\" — Carlos Pérez, 23/08\n\n📱 Actividad en app:\n- Logins esta semana: 5\n- Clases consultadas: 8\n\n🏷️ Estado: Activo — Alta frecuencia\n```\n\n**Ejemplo renderizado (Roberto García — solicitud manual, con entrenamiento libre):**\n\n```\n📋 RESUMEN DE ASISTENCIA — Roberto García\n\n📅 Período: 01/08/2026 – 23/08/2026\n📊 Total de clases asistidas: 3 (Clases grupales) + 6 (Entrenamiento libre)\n🏋️ Clases más frecuentes: Boxeo Cardio (3)\n👤 Instructor principal: Miguel Torres\n⏰ Horario preferido: Tarde (16:00 – 18:00)\n⏱️ Duración promedio: 90 minutos\n🔥 Racha actual: 2 días seguidos (19/08 – 22/08)\n📅 Primera asistencia: 05/08/2026\n📅 Última asistencia: 22/08/2026\n🚫 Días sin asistencia: 17\n\n📝 Notas del entrenador:\n- \"Roberto mejoró su técnica de combinaciones. Pide entrenamiento de fuerza adicional.\" — Miguel Torres, 15/08\n- \"Roberto está listo para spar. Recomiendo sesión de evaluación de fuerza.\" — Miguel Torres, 22/08\n\n📱 Actividad en app:\n- Logins esta semana: 3\n- Clases consultadas: 5\n\n🏷️ Estado: Activo — Frecuencia moderada\n```\n\n**Ejemplo renderizado (con evaluación física):**\n\n```\n📋 RESUMEN DE ASISTENCIA — Pedro Sánchez\n\n📅 Período: 01/08/2026 – 23/08/2026\n📊 Total de clases asistidas: 10\n🏋️ Clases más frecuentes: Fuerza (6), Cardio (4)\n👤 Instructor principal: Ana Martínez\n⏰ Horario preferido: Mañana (07:00 – 10:00)\n⏱️ Duración promedio: 72 minutos\n🔥 Racha actual: 5 días seguidos\n📅 Primera asistencia: 01/08/2026\n📅 Última asistencia: 23/08/2026\n🚫 Días sin asistencia: 5\n\n📝 Notas del entrenador:\n- \"Pedro aumentó 5kg en press de banca\" — Ana Martínez, 15/08\n\n📊 Evaluación física:\n- Fecha: 15/08/2026\n- Peso: 82.0 kg\n- IMC: 26.1\n- % Grasa: 18.5%\n- Masa muscular: 35.2 kg\n- Fuerza: Avanzado\n- Resistencia: Buena\n- Flexibilidad: Promedio\n- Notas: \"Pedro mejoró su fuerza de tren superior. Reducir peso muerto hasta técnica perfecta.\"\n\n📱 Actividad en app:\n- Logins esta semana: 6\n- Clases consultadas: 10\n\n🏷️ Estado: Activo — Alta frecuencia\n```\n\n---\n\n## 2. Notificación al responsable humano\n\n**Cuándo se genera:** Paso 5 — Cuando el resumen está listo para notificar.\n\n**Notificación por email:**\n\n**Asunto:** Resumen de asistencia listo — [nombre_miembro] | [gimnasio_nombre]\n\n```\nHola [responsable_nombre],\n\nEl resumen de asistencia de [nombre_miembro] está listo.\n\n📋 Período: [periodo_inicio_formateado] – [periodo_fin_formateado]\n📊 Clases asistidas: [total_clases_asistidas]\n🔥 Racha: [racha_dias_seguidos] días seguidos\n\nResumen completo:\n\n[texto_del_resumen]\n\nRegistro guardado: [registro_id]\n— [gimnasio_nombre]\n```\n\n**Ejemplo renderizado:**\n\n```\nAsunto: Resumen de asistencia listo — María López | FitZone Centro\n\nHola Carlos Pérez,\n\nEl resumen de asistencia de María López está listo.\n\n📋 Período: 17/08/2026 – 23/08/2026\n📊 Clases asistidas: 7\n🔥 Racha: 7 días seguidos\n\nResumen completo:\n\n📋 RESUMEN DE ASISTENCIA — María López\n\n📅 Período: 17/08/2026 – 23/08/2026\n📊 Total de clases asistidas: 7\n🏋️ Clases más frecuentes: Spinning (5), Yoga (1), Funcional (1)\n👤 Instructor principal: Carlos Pérez\n⏰ Horario preferido: Mañana (07:00 – 10:00)\n⏱️ Duración promedio: 84 minutos\n🔥 Racha actual: 7 días seguidos\n📅 Primera asistencia: 17/08/2026\n📅 Última asistencia: 23/08/2026\n🚫 Días sin asistencia: 0\n\n📝 Notas del entrenador:\n- \"María mejoró su flexibilidad en postura de paloma\" — Laura Gómez, 19/08\n- \"María aumentó la resistencia, mantuvo ritmo alto todo el clase\" — Carlos Pérez, 23/08\n\n📱 Actividad en app:\n- Logins esta semana: 5\n- Clases consultadas: 8\n\n🏷️ Estado: Activo — Alta frecuencia\n\nRegistro guardado: AR-20260823200000\n— FitZone Centro\n```\n\n**Notificación por WhatsApp/SMS:**\n\n```\n📋 Resumen de asistencia listo para [nombre_miembro]\n\n📊 Período: [periodo_inicio_formateado] – [periodo_fin_formateado]\n🏋️ Clases: [total_clases_asistidas]\n🔥 Racha: [racha_dias_seguidos] días\n\nResumen enviado por email. Registro: [registro_id]\n— [gimnasio_nombre]\n```\n\n**Ejemplo renderizado (WhatsApp):**\n\n```\n📋 Resumen de asistencia listo para María López\n\n📊 Período: 17/08/2026 – 23/08/2026\n🏋️ Clases: 7\n🔥 Racha: 7 días\n\nResumen enviado por email. Registro: AR-20260823200000\n— FitZone Centro\n```\n\n**Notificación al miembro (cuando él solicitó):**\n\n```\n¡Hola [nombre_miembro]! Tu resumen de asistencia está listo 📋\n\n📊 Período: [periodo_inicio_formateado] – [periodo_fin_formateado]\n🏋️ Clases asistidas: [total_clases_asistidas]\n🔥 Racha actual: [racha_dias_seguidos] días seguidos\n\nPodes ver el resumen completo en la app o pedírnoslo por este canal.\n¡Sigue así! 💪\n— [gimnasio_nombre]\n```\n\n**Ejemplo renderizado (miembro - WhatsApp):**\n\n```\n¡Hola María López! Tu resumen de asistencia está listo 📋\n\n📊 Período: 17/08/2026 – 23/08/2026\n🏋️ Clases asistidas: 7\n🔥 Racha actual: 7 días seguidos\n\nPodes ver el resumen completo en la app o pedírnoslo por este canal.\n¡Sigue así! 💪\n— FitZone Centro\n```\n\n---\n\n## 3. Registro de acción (JSON)\n\n**Cuándo se genera:** En cada paso del flujo, como registro interno.\n\n**Estructura del registro:**\n\n```json\n{\n  \"registro_id\": \"AR-[timestamp]\",\n  \"agente\": \"attendance-recap-agent\",\n  \"evento_id\": \"[evento_id]\",\n  \"contacto_id\": \"[contacto_id]\",\n  \"nombre_miembro\": \"[nombre_miembro]\",\n  \"gimnasio_id\": \"[gimnasio_id]\",\n  \"gimnasio_nombre\": \"[gimnasio_nombre]\",\n  \"tipo_generacion\": \"[tipo_generacion]\",\n  \"periodo_inicio\": \"[periodo_inicio]\",\n  \"periodo_fin\": \"[periodo_fin]\",\n  \"fecha_hora_recepcion\": \"[fecha_hora_recepcion]\",\n  \"total_clases_asistidas\": \"[total_clases_asistidas]\",\n  \"clase_mas_frecuente\": \"[clase_mas_frecuente]\",\n  \"instructor_mas_frecuente\": \"[instructor_mas_frecuente]\",\n  \"horario_preferido\": \"[horario_preferido]\",\n  \"racha_dias_seguidos\": \"[racha_dias_seguidos]\",\n  \"estado_miembro\": \"[estado_miembro]\",\n  \"tiene_notas_entrenador\": \"[true/false]\",\n  \"tiene_evaluacion\": \"[true/false]\",\n  \"responsable_notificacion\": \"[responsable_notificacion]\",\n  \"canal_notificacion\": \"[canal_notificacion]\",\n  \"fecha_hora_guardado\": \"[fecha_hora_guardado]\",\n  \"fecha_hora_notificacion\": \"[fecha_hora_notificacion]\",\n  \"resultado\": \"[exitoso | datos_insuficientes | error_envio]\",\n  \"timestamp_registro\": \"[tiempo_fecha_actual]\"\n}\n```\n\n**Ejemplo de registro completo (exitoso):**\n\n```json\n{\n  \"registro_id\": \"AR-20260823200000\",\n  \"agente\": \"attendance-recap-agent\",\n  \"evento_id\": \"REC-20260823-200000-001\",\n  \"contacto_id\": \"CT-2026-00452\",\n  \"nombre_miembro\": \"María López\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"tipo_generacion\": \"semanal\",\n  \"periodo_inicio\": \"2026-08-17\",\n  \"periodo_fin\": \"2026-08-23\",\n  \"fecha_hora_recepcion\": \"2026-08-23T20:00:00Z\",\n  \"total_clases_asistidas\": 7,\n  \"clase_mas_frecuente\": \"Spinning\",\n  \"instructor_mas_frecuente\": \"Carlos Pérez\",\n  \"horario_preferido\": \"Mañana (07:00 – 10:00)\",\n  \"racha_dias_seguidos\": 7,\n  \"estado_miembro\": \"Activo — Alta frecuencia\",\n  \"tiene_notas_entrenador\": true,\n  \"tiene_evaluacion\": false,\n  \"responsable_notificacion\": \"Carlos Pérez (Entrenador principal)\",\n  \"canal_notificacion\": \"email\",\n  \"fecha_hora_guardado\": \"2026-08-23T20:00:05Z\",\n  \"fecha_hora_notificacion\": \"2026-08-23T20:00:10Z\",\n  \"resultado\": \"exitoso\",\n  \"timestamp_registro\": \"2026-08-23T20:00:10Z\"\n}\n```\n\n**Ejemplo de registro (datos insuficientes — solo check-ins sin clases):**\n\n```json\n{\n  \"registro_id\": \"AR-20260823200001\",\n  \"agente\": \"attendance-recap-agent\",\n  \"evento_id\": \"REC-20260823-200000-002\",\n  \"contacto_id\": \"CT-2026-00501\",\n  \"nombre_miembro\": \"Lucas Fernández\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"tipo_generacion\": \"solicitud_manual\",\n  \"periodo_inicio\": \"2026-08-01\",\n  \"periodo_fin\": \"2026-08-23\",\n  \"fecha_hora_recepcion\": \"2026-08-23T14:00:00Z\",\n  \"total_clases_asistidas\": 0,\n  \"clase_mas_frecuente\": \"N/D\",\n  \"instructor_mas_frecuente\": \"N/D\",\n  \"horario_preferido\": \"N/D\",\n  \"racha_dias_seguidos\": 0,\n  \"estado_miembro\": \"Inactivo\",\n  \"tiene_notas_entrenador\": false,\n  \"tiene_evaluacion\": false,\n  \"responsable_notificacion\": \"Lucas Fernández (Miembro)\",\n  \"canal_notificacion\": \"whatsapp\",\n  \"fecha_hora_guardado\": \"2026-08-23T14:00:05Z\",\n  \"fecha_hora_notificacion\": \"2026-08-23T14:00:10Z\",\n  \"resultado\": \"exitoso\",\n  \"timestamp_registro\": \"2026-08-23T14:00:10Z\"\n}\n```\n\n---\n\n## 4. Registro de escalamiento\n\n**Cuándo se genera:** Cuando el agente escala el caso a un humano.\n\n**Estructura:**\n\n```json\n{\n  \"escalamiento_id\": \"AR-ESC-[timestamp]\",\n  \"agente\": \"attendance-recap-agent\",\n  \"gimnasio_id\": \"[gimnasio_id]\",\n  \"gimnasio_nombre\": \"[gimnasio_nombre]\",\n  \"contacto_id\": \"[contacto_id]\",\n  \"nombre_miembro\": \"[nombre_miembro]\",\n  \"motivo_escalamiento\": \"[motivo_enum]\",\n  \"descripcion_caso\": \"[texto_descriptivo]\",\n  \"urgencia\": \"[alta | media | baja]\",\n  \"asignado_a\": \"[nombre_humano_asignado]\",\n  \"timestamp_escalamiento\": \"[tiempo_fecha_actual]\",\n  \"datos_contexto\": {\n    \"tipo_generacion\": \"[tipo_generacion]\",\n    \"periodo\": \"[periodo_inicio] – [periodo_fin]\",\n    \"total_clases\": \"[total_clases_asistidas]\",\n    \"resultado_hasta_momento\": \"[resultado_enum]\"\n  }\n}\n```\n\n**Ejemplo de registro de escalamiento:**\n\n```json\n{\n  \"escalamiento_id\": \"AR-ESC-20260823210000\",\n  \"agente\": \"attendance-recap-agent\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"contacto_id\": \"CT-2026-00601\",\n  \"nombre_miembro\": \"Pedro Sánchez\",\n  \"motivo_escalamiento\": \"datos_contradictorios\",\n  \"descripcion_caso\": \"Los datos de check-in muestran 10 visitas al gimnasio pero los datos de clases muestran solo 2 clases asistidas. El instructor Ana Martínez reportó 8 sesiones de entrenamiento personal, pero no hay registro de check-in correspondiente. Los datos son inconsistentes y requieren verificación manual.\",\n  \"urgencia\": \"media\",\n  \"asignado_a\": \"Gerencia FitZone Centro\",\n  \"timestamp_escalamiento\": \"2026-08-23T21:00:00Z\",\n  \"datos_contexto\": {\n    \"tipo_generacion\": \"mensual\",\n    \"periodo\": \"2026-08-01 – 2026-08-23\",\n    \"total_clases\": 10,\n    \"resultado_hasta_momento\": \"error_datos\"\n  }\n}\n```\n\n---\n\n## 5. Formato de fechas\n\n| Campo | Formato | Ejemplo |\n|---|---|---|\n| Período | `DD/MM/YYYY – DD/MM/YYYY` | `\"17/08/2026 – 23/08/2026\"` |\n| Fecha individual | `DD/MM/YYYY` | `\"23/08/2026\"` |\n| Fecha ISO 8601 | `YYYY-MM-DDTHH:MM:SS` | `\"2026-08-23T20:00:00Z\"` |\n| Hora | `HH:MM` | `\"20:00\"` |\n\n---\n*Plantilla generada para el nicho Gimnasios y Fitness — Agente Attendance Recap Agent*\n"
              },
              {
                "id": "attendance-recap-agent/prompt-sistema.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "prompt-sistema.md",
                "contenido": "# Prompt del Sistema — Attendance Recap Agent\n\n## Identidad\nEres **Attendance Recap Agent**, un asistente automatizado de **[Nombre del Gimnasio]** especializado en resumir información dispersa sobre la actividad de asistencia de un miembro. Tu función es ejecutar el flujo de 5 pasos exactos que se define a continuación.\n\n## Tono de comunicación\n- Claro, profesional y orientado a datos\n- Usa viñetas y encabezados fijos para facilitar el escaneo\n- Incluye cifras concretas, no descripciones vagas\n- Cita textualmente las notas del entrenador\n\n## Flujo de ejecución\n\nEl agente ejecuta estos 5 pasos en orden, sin bifurcaciones ni condicionales:\n\n### Paso 1: Recibe el conjunto de datos o transcripción de entrada\nEl agente recibe los datos de asistencia del miembro: check-ins, clases, notas de entrenador, actividad en app, evaluaciones físicas.\n\n### Paso 2: Extrae los puntos clave según una lista fija de campos\nEl agente procesa cada fuente de datos y extrae valores para 14 campos fijos: total de clases, clases por tipo, clase más frecuente, instructor más frecuente, horario preferido, duración promedio, total de minutos, racha de días, primera y última asistencia, días sin asistencia, notas del entrenador, actividad en app, evaluación física.\n\n### Paso 3: Redacta el resumen en formato de lista con encabezados fijos\nEl agente genera el resumen usando la plantilla exacta, insertando los datos extraídos en cada posición correspondiente.\n\n### Paso 4: Guarda el resumen en el registro del contacto o caso correspondiente\nEl agente almacena el resumen completo (texto plano + datos estructurados) en el registro del contacto.\n\n### Paso 5: Notifica al responsable humano cuando el resumen esté listo\nEl agente envía la notificación al responsable configurado por el canal indicado.\n\n## Ejemplo de ejecución\n\n**Entrada:**\n```\ncontacto_id: \"CT-2026-00452\"\nnombre_miembro: \"María López\"\ntipo_generacion: \"semanal\"\nperiodo_inicio: \"2026-08-17\"\nperiodo_fin: \"2026-08-23\"\n```\n\n**Ejecución paso a paso:**\n\n**Paso 1:** Recibe datos de 7 check-ins, 7 clases, 2 notas de entrenador y actividad en app.\n\n**Paso 2:** Extrae:\n- Total clases: 7\n- Por tipo: Spinning (5), Yoga (1), Funcional (1)\n- Más frecuente: Spinning\n- Instructor: Carlos Pérez\n- Horario: Mañana (07:00–10:00)\n- Duración promedio: 84 min\n- Racha: 7 días\n- Días sin asistencia: 0\n\n**Paso 3:** Genera resumen con encabezados fijos (📋📅📊🏋️👤⏰⏱️🔥🚫📝📱🏷️).\n\n**Paso 4:** Guarda como registro `AR-20260823200000` en el perfil del contacto.\n\n**Paso 5:** Envía notificación a Carlos Pérez (entrenador principal) por email.\n\n## Fuentes de datos\n\n| Código | Fuente | Tipo de datos |\n|--------|--------|---------------|\n| `checkin` | Registro de check-in del gimnasio | Fecha, hora, duración, área |\n| `clase` | Registro de asistencia a clases | Clase, instructor, sala, duración |\n| `entrenador` | Notas del entrenador personal | Texto libre sobre progreso |\n| `app` | Actividad en la app del gimnasio | Login, uso de funciones |\n| `evaluacion` | Evaluaciones físicas periódicas | Peso, medidas, % grasa |\n\n## Restricciones\n1. Ejecutar solo los 5 pasos del flujo, sin agregar pasos adicionales\n2. Extraer solo datos confirmados, sin suposiciones\n3. Usar siempre los encabezados fijos definidos\n4. Marcar datos faltantes con `N/D`\n5. Citar textualmente las notas del entrenador\n6. No compartir información de otros miembros\n\n## Reglas de escalamiento\nEl agente escala a un humano en las siguientes situaciones:\n\n| Condición | Tiempo de respuesta |\n|-----------|---------------------|\n| Datos contradictorios entre fuentes | 1 hora |\n| Miembro solicita eliminar su información | 15 minutos |\n| Error en el proceso de guardado del registro | 30 minutos |\n| Notas del entrenador contienen información sensible | 1 hora |\n| Total de clases asistidas es 0 en un período mayor a 30 días | 24 horas |\n"
              },
              {
                "id": "attendance-recap-agent/reglas-escalamiento.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "reglas-escalamiento.md",
                "contenido": "# Reglas de escalamiento — Attendance Recap Agent\n\n## Descripción\n\nLista las condiciones exactas bajo las cuales el agente debe transferir el caso a un humano, sin dejar casos ambiguos. Cada condición tiene un identificador único, la situación que la activa, la acción a tomar y el plazo de respuesta.\n\n---\n\n## Condiciones de escalamiento\n\n### Categoría 1: Datos inconsistentes o contradictorios\n\n| ID | Condición | Acción del agente | Urgencia | Plazo de respuesta |\n|----|-----------|-------------------|----------|-------------------|\n| C1.1 | Los datos de check-in y los datos de clases no coinciden (diferencia >30%) | Escalar a [equipo de datos] con ambos conjuntos de datos para verificación. Generar resumen parcial con los datos disponibles. | Media | 24 horas |\n| C1.2 | Una nota del entrenador contradice los datos de asistencia (ej. \"asistió poco\" pero hay 15 check-ins) | Escalar a [entrenador responsable] para verificar la nota. Generar resumen con los datos numéricos y marcar la nota como \"pendiente de verificación\". | Media | 24 horas |\n| C1.3 | El `duracion_minutos` de un check-in es mayor al horario de apertura del gimnasio | Escalar a [equipo técnico] para verificar el registro. Generar resumen excluyendo el dato inválido. | Baja | 48 horas |\n| C1.4 | Hay más de 2 check-ins en el mismo día con horas superpuestas | Escalar a [equipo de datos] para verificar si es error del sistema o visita múltiple. | Media | 24 horas |\n\n### Categoría 2: Datos faltantes críticos\n\n| ID | Condición | Acción del agente | Urgencia | Plazo de respuesta |\n|----|-----------|-------------------|----------|-------------------|\n| C2.1 | No hay ningún dato de check-in, clases, notas ni app (todas las fuentes son `null` o arrays vacíos) | No generar resumen. Escalar a [equipo de datos] para verificar por qué no hay datos del miembro. | Media | 24 horas |\n| C2.2 | El `contacto_id` no existe en la base de datos del CRM | No generar resumen. Escalar a [equipo de datos] para verificar el registro. | Baja | 48 horas |\n| C2.3 | El período solicitado es mayor a 90 días | No generar resumen. Informar al solicitante que el máximo es 90 días. Si la solicitud es manual del miembro, notificar al equipo de datos. | Baja | 48 horas |\n\n### Categoría 3: Problemas de notificación\n\n| ID | Condición | Acción del agente | Urgencia | Plazo de respuesta |\n|----|-----------|-------------------|----------|-------------------|\n| C3.1 | El envío de notificación falla dos veces consecutivas | Registrar el error. El resumen ya está guardado. Escalar a [equipo técnico] para verificar el canal de notificación. | Baja | 48 horas |\n| C3.2 | El canal de notificación no está configurado o es inválido | Escalar a [equipo técnico] para verificar la configuración. Usar `email` como canal de respaldo si el email está disponible. | Baja | 48 horas |\n| C3.3 | El email o teléfono del responsable no tiene formato válido | Escalar a [equipo de datos] para verificar el registro del responsable. Generar el resumen y guardarlo sin notificar hasta resolver. | Baja | 48 horas |\n\n### Categoría 4: Solicitudes especiales\n\n| ID | Condición | Acción del agente | Urgencia | Plazo de respuesta |\n|----|-----------|-------------------|----------|-------------------|\n| C4.1 | El solicitante pide un período específico que no coincide con los tipos predefinidos (semanal/mensual) | Aceptar la solicitud y generar el resumen para el período exacto indicado. No escalar. | Baja | No requiere |\n| C4.2 | El miembro solicita que su información de asistencia no sea compartida con entrenadores | Registrar la preferencia. Generar el resumen sin notas de entrenador. Notificar a [equipo de datos] para actualizar el perfil. | Baja | 48 horas |\n| C4.3 | El solicitante es un tercero que no es el miembro ni un empleado del gimnasio | No generar el resumen. Escalar a [gerencia] para verificar autorización. | Alta | 4 horas |\n\n### Categoría 5: Errores del sistema\n\n| ID | Condición | Acción del agente | Urgencia | Plazo de respuesta |\n|----|-----------|-------------------|----------|-------------------|\n| C5.1 | El sistema de check-in reporta más registros que el número máximo configurado (>500 en un período) | Generar resumen con los primeros 500 registros. Escalar a [equipo técnico] para verificar el límite del sistema. | Media | 24 horas |\n| C5.2 | Los datos recibidos contienen caracteres especiales o codificación incorrecta | Limpiar los datos y generar el resumen. Escalar a [equipo técnico] si la limpieza afecta más del 20% de los campos. | Baja | 48 horas |\n| C5.3 | El `evento_id` recibido tiene formato inválido | No procesar. Escalar a [equipo técnico] para verificar el sistema de eventos. | Baja | 48 horas |\n\n---\n\n## Reglas de asignación de urgencia\n\n| Nivel | Color | Significado | Tiempo máximo de espera |\n|-------|-------|-------------|------------------------|\n| **Alta** | 🔴 | Requiere intervención humana inmediata para resolver un problema que afecta datos sensibles o privacidad | 4 horas |\n| **Media** | 🟡 | Requiere revisión humana para resolver la inconsistencia de datos correctamente | 24 horas |\n| **Baja** | 🟢 | Informativo, requiere seguimiento pero el resumen puede generarse con los datos disponibles | 48 horas |\n\n---\n\n## Reglas de notificación por urgencia\n\n| Urgencia | Canal de notificación | Destinatario |\n|----------|----------------------|--------------|\n| **Alta** | WhatsApp + Email | [Responsable directo] + [Supervisor] |\n| **Media** | Email | [Responsable directo] |\n| **Baja** | Registro en sistema | [Equipo de datos] |\n\n---\n\n## Textos base para escalamiento\n\n### Al responsable (cuando se escala por datos inconsistentes):\n\n```\nHola [responsable_nombre],\n\nEl resumen de asistencia de [nombre_miembro] requiere verificación. Se detectaron inconsistencias en los datos recibidos.\n\n⚠️ Motivo: [motivo_escalamiento]\n📝 Detalle: [descripcion_caso]\n\nEl resumen se ha generado con los datos disponibles. Por favor, revisá y confirmá la información.\n— [gimnasio_nombre]\n```\n\n**Ejemplo:**\n\n```\nHola Carlos Pérez,\n\nEl resumen de asistencia de Pedro Sánchez requiere verificación. Se detectaron inconsistencias en los datos recibidos.\n\n⚠️ Motivo: datos_contradictorios\n📝 Detalle: Los datos de check-in muestran 10 visitas al gimnasio pero los datos de clases muestran solo 2 clases asistidas. El instructor Ana Martínez reportó 8 sesiones de entrenamiento personal, pero no hay registro de check-in correspondiente.\n\nEl resumen se ha generado con los datos disponibles. Por favor, revisá y confirmá la información.\n— FitZone Centro\n```\n\n### Al equipo interno (notificación de escalamiento):\n\n```\n🚨 ESCALAMIENTO — Attendance Recap Agent\n\n📋 Caso: [escalamiento_id]\n👤 Miembro: [nombre_miembro] ([contacto_id])\n🏋️ Gimnasio: [gimnasio_nombre] ([gimnasio_id])\n📅 Período: [periodo]\n\n⚠️ Motivo: [motivo_escalamiento]\n📝 Detalle: [descripcion_caso]\n🔴 Urgencia: [urgencia]\n⏰ Plazo de respuesta: [plazo] horas\n👤 Asignado a: [asignado_a]\n\n📊 Datos disponibles:\n- Check-ins: [total_checkins]\n- Clases: [total_clases]\n- Notas entrenador: [total_notas]\n- App: [actividad_app]\n```\n\n**Ejemplo:**\n\n```\n🚨 ESCALAMIENTO — Attendance Recap Agent\n\n📋 Caso: AR-ESC-20260823210000\n👤 Miembro: Pedro Sánchez (CT-2026-00601)\n🏋️ Gimnasio: FitZone Centro (GYM-001)\n📅 Período: 2026-08-01 – 2026-08-23\n\n⚠️ Motivo: datos_contradictorios\n📝 Detalle: Los datos de check-in muestran 10 visitas al gimnasio pero los datos de clases muestran solo 2 clases asistidas. El instructor Ana Martínez reportó 8 sesiones de entrenamiento personal, pero no hay registro de check-in correspondiente. Los datos son inconsistentes y requieren verificación manual.\n🔴 Urgencia: media\n⏰ Plazo de respuesta: 24 horas\n👤 Asignado a: Gerencia FitZone Centro\n\n📊 Datos disponibles:\n- Check-ins: 10\n- Clases: 2\n- Notas entrenador: 1\n- App: N/D\n```\n\n### Al miembro (cuando se escala y él solicitó el resumen):\n\n```\n¡Hola [nombre_miembro]! Tu resumen de asistencia está casi listo.\n\nDetectamos una pequeña inconsistencia en los datos y nuestro equipo la está revisando. Te lo enviamos en un máximo de [plazo] horas.\n\nMientras tanto, seguí entrenando 💪\n— [gimnasio_nombre]\n```\n\n**Ejemplo:**\n\n```\n¡Hola Pedro Sánchez! Tu resumen de asistencia está casi listo.\n\nDetectamos una pequeña inconsistencia en los datos y nuestro equipo la está revisando. Te lo enviamos en un máximo de 24 horas.\n\nMientras tanto, seguí entrenando 💪\n— FitZone Centro\n```\n\n---\n\n## Ejemplo completo de escalamiento\n\n**Caso:** Los datos de check-in y clases no coinciden para el miembro Pedro Sánchez.\n\n**Registro de escalamiento generado:**\n\n```json\n{\n  \"escalamiento_id\": \"AR-ESC-20260823210000\",\n  \"agente\": \"attendance-recap-agent\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"contacto_id\": \"CT-2026-00601\",\n  \"nombre_miembro\": \"Pedro Sánchez\",\n  \"motivo_escalamiento\": \"datos_contradictorios\",\n  \"descripcion_caso\": \"Los datos de check-in muestran 10 visitas al gimnasio pero los datos de clases muestran solo 2 clases asistidas. El instructor Ana Martínez reportó 8 sesiones de entrenamiento personal, pero no hay registro de check-in correspondiente. Los datos son inconsistentes y requieren verificación manual.\",\n  \"urgencia\": \"media\",\n  \"asignado_a\": \"Gerencia FitZone Centro\",\n  \"timestamp_escalamiento\": \"2026-08-23T21:00:00Z\",\n  \"datos_contexto\": {\n    \"tipo_generacion\": \"mensual\",\n    \"periodo\": \"2026-08-01 – 2026-08-23\",\n    \"total_clases\": 10,\n    \"resultado_hasta_momento\": \"error_datos\"\n  }\n}\n```\n\n**Acciones tomadas:**\n1. Se generó un resumen parcial con los datos numéricos disponibles.\n2. Se notificó a Gerencia FitZone Centro por email.\n3. Las notas del entrenador se marcaron como \"pendiente de verificación\".\n4. El resumen se guardó en el registro del contacto con el estado `parcial_verificacion_pendiente`.\n\n---\n*Plantilla generada para el nicho Gimnasios y Fitness — Agente Attendance Recap Agent*\n"
              },
              {
                "id": "attendance-recap-agent/variables-entrada.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "variables-entrada.md",
                "contenido": "# Variables de entrada — Attendance Recap Agent\n\nEste archivo define la lista fija y cerrada de campos de datos que el agente necesita recibir para operar. No se admiten campos adicionales a los listados aquí.\n\n---\n\n## Campos del evento disparador\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 1 | `evento_id` | `string` | Obligatorio | Identificador único del evento. Formato: `REC-YYYYMMDD-HHMMSS-XXX`. | `\"REC-20260823-200000-001\"` |\n| 2 | `contacto_id` | `string` | Obligatorio | Identificador del contacto asociado al evento. Formato: `CT-YYYY-XXXXX`. | `\"CT-2026-00452\"` |\n| 3 | `tipo_generacion` | `string` (enum) | Obligatorio | Tipo de evento que dispara el resumen. Valores: `semanal`, `mensual`, `solicitud_manual`, `post_evaluacion`. | `\"semanal\"` |\n| 4 | `fecha_hora_solicitud` | `string` (ISO 8601) | Obligatorio | Fecha y hora en que se solicitó o programó el resumen. | `\"2026-08-23T20:00:00Z\"` |\n\n---\n\n## Datos del miembro\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 5 | `nombre_miembro` | `string` | Obligatorio | Nombre completo del miembro. | `\"María López\"` |\n| 6 | `telefono_miembro` | `string` | Opcional (requerido si `canal_notificacion` es `whatsapp` o `sms`) | Número telefónico del miembro. Entre 8 y 15 dígitos con código de país. | `\"+54 11 5555-1234\"` |\n| 7 | `email_miembro` | `string` | Opcional (requerido si `canal_notificacion` es `email`) | Correo electrónico del miembro. | `\"maria.lopez@email.com\"` |\n| 8 | `membresia_tipo` | `string` (enum) | Opcional | Tipo de membresía del miembro. Valores: `premium`, `basico`, `estudiante`, `corporativo`. | `\"premium\"` |\n| 9 | `membresia_estado` | `string` (enum) | Opcional | Estado de la membresía. Valores: `activa`, `pausada`, `vencida`. | `\"activa\"` |\n\n---\n\n## Datos de check-in\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 10 | `datos_checkin` | `array` de objetos | Opcional | Lista de registros de check-in del miembro en el período. Cada objeto contiene los campos del check-in. | Ver estructura abajo |\n\n**Estructura de cada objeto en `datos_checkin`:**\n\n| Campo | Tipo | Descripción | Ejemplo |\n|---|---|---|---|\n| `fecha` | `string` (YYYY-MM-DD) | Fecha del check-in | `\"2026-08-23\"` |\n| `hora_entrada` | `string` (HH:MM) | Hora de entrada | `\"08:15\"` |\n| `hora_salida` | `string` (HH:MM) | Hora de salida | `\"09:45\"` |\n| `duracion_minutos` | `integer` | Duración de la visita en minutos | `90` |\n| `area` | `string` | Área o zona del gimnasio visitada | `\"Sala de spinning\"` |\n\n---\n\n## Datos de clases asistidas\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 11 | `datos_clases` | `array` de objetos | Opcional | Lista de clases asistidas por el miembro en el período. | Ver estructura abajo |\n\n**Estructura de cada objeto en `datos_clases`:**\n\n| Campo | Tipo | Descripción | Ejemplo |\n|---|---|---|---|\n| `fecha` | `string` (YYYY-MM-DD) | Fecha de la clase | `\"2026-08-23\"` |\n| `clase` | `string` | Nombre de la clase | `\"Spinning Matutino\"` |\n| `instructor` | `string` | Nombre del instructor | `\"Carlos Pérez\"` |\n| `sala` | `string` | Sala o área donde se realizó | `\"Sala 3\"` |\n| `duracion_minutos` | `integer` | Duración de la clase en minutos | `60` |\n\n---\n\n## Notas del entrenador\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 12 | `notas_entrenador` | `array` de objetos | Opcional | Notas o comentarios dejados por el entrenador sobre el miembro. | Ver estructura abajo |\n\n**Estructura de cada objeto en `notas_entrenador`:**\n\n| Campo | Tipo | Descripción | Ejemplo |\n|---|---|---|---|\n| `fecha` | `string` (YYYY-MM-DD) | Fecha de la nota | `\"2026-08-23\"` |\n| `instructor` | `string` | Nombre del instructor que dejó la nota | `\"Carlos Pérez\"` |\n| `nota` | `string` | Texto de la nota | `\"María aumentó la resistencia\"` |\n\n---\n\n## Datos de actividad en app\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 13 | `datos_app` | `object` | Opcional | Actividad del miembro en la app del gimnasio. | Ver estructura abajo |\n\n**Estructura de `datos_app`:**\n\n| Campo | Tipo | Descripción | Ejemplo |\n|---|---|---|---|\n| `logins_semana` | `integer` | Número de veces que abrió la app esta semana | `5` |\n| `clases_consultadas` | `integer` | Número de clases que consultó en la app | `8` |\n| `perfil_vistas` | `integer` | Número de veces que vio su perfil | `2` |\n\n---\n\n## Datos de evaluación física\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 14 | `datos_evaluacion` | `object` o `null` | Opcional | Datos de la evaluación física más reciente del miembro (si aplica). `null` si no hay evaluación en el período. | Ver estructura abajo |\n\n**Estructura de `datos_evaluacion`:**\n\n| Campo | Tipo | Descripción | Ejemplo |\n|---|---|---|---|\n| `fecha` | `string` (YYYY-MM-DD) | Fecha de la evaluación | `\"2026-08-15\"` |\n| `peso_kg` | `number` | Peso en kilogramos | `68.5` |\n| `altura_cm` | `number` | Altura en centímetros | `165` |\n| `imc` | `number` | Índice de masa corporal | `25.2` |\n| `porcentaje_grasa` | `number` | Porcentaje de grasa corporal | `22.0` |\n| `masa_muscular_kg` | `number` | Masa muscular en kilogramos | `28.5` |\n| `test_fuerza` | `string` | Resultado del test de fuerza | `\"Intermedio\"` |\n| `test_resistencia` | `string` | Resultado del test de resistencia | `\"Bueno\"` |\n| `test_flexibilidad` | `string` | Resultado del test de flexibilidad | `\"Promedio\"` |\n| `notas` | `string` | Notas del evaluador | `\"María mejoró su fuerza de tren inferior\"` |\n\n---\n\n## Datos del período\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 15 | `periodo_inicio` | `string` (YYYY-MM-DD) | Obligatorio | Fecha de inicio del período a resumir. | `\"2026-08-17\"` |\n| 16 | `periodo_fin` | `string` (YYYY-MM-DD) | Obligatorio | Fecha de fin del período a resumir. | `\"2026-08-23\"` |\n\n---\n\n## Datos del gimnasio\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 17 | `gimnasio_id` | `string` | Obligatorio | Identificador único del gimnasio. | `\"GYM-001\"` |\n| 18 | `gimnasio_nombre` | `string` | Obligatorio | Nombre comercial del gimnasio. | `\"FitZone Centro\"` |\n| 19 | `gimnasio_telefono` | `string` | Opcional | Teléfono del gimnasio. | `\"+54 11 4444-5678\"` |\n\n---\n\n## Datos de notificación\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 20 | `responsable_notificacion` | `string` | Obligatorio | Nombre de la persona que debe recibir la notificación (entrenador, gerente o el propio miembro). | `\"Carlos Pérez (Entrenador principal)\"` |\n| 21 | `canal_notificacion` | `string` (enum) | Obligatorio | Canal para enviar la notificación. Valores: `whatsapp`, `sms`, `email`, `app`. | `\"email\"` |\n| 22 | `email_responsable` | `string` | Condicional (requerido si `canal_notificacion` es `email`) | Correo electrónico del responsable. | `\"carlos.perez@fitzone.com\"` |\n| 23 | `telefono_responsable` | `string` | Condicional (requerido si `canal_notificacion` es `whatsapp` o `sms`) | Teléfono del responsable. | `\"+54 11 4444-5678\"` |\n\n---\n\n## Configuración de tiempos\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 24 | `tiempo_fecha_actual` | `string` (ISO 8601) | Obligatorio | Fecha y hora actual del sistema. | `\"2026-08-23T20:00:00Z\"` |\n| 25 | `hora_preferida_notificacion` | `string` (HH:MM) | Opcional | Hora preferida para enviar notificaciones. Valor por defecto: `\"20:00\"`. | `\"20:00\"` |\n| 26 | `horario_atencion_inicio` | `string` (HH:MM) | Opcional | Hora de inicio del horario de atención. Valor por defecto: `\"07:00\"`. | `\"07:00\"` |\n| 27 | `horario_atencion_fin` | `string` (HH:MM) | Opcional | Hora de fin del horario de atención. Valor por defecto: `\"22:00\"`. | `\"22:00\"` |\n\n---\n\n## Campos calculados por el agente\n\nEstos campos **no se reciben de entrada**; el agente los genera internamente.\n\n| # | Nombre del campo | Tipo de dato | Generado en | Descripción |\n|---|---|---|---|---|\n| 28 | `registro_id` | `string` | Paso 4 | ID único del registro. Formato: `AR-YYYYMMDD-HHMMSS-XXX`. |\n| 29 | `fecha_hora_recepcion` | `string` (ISO 8601) | Paso 1 | Marca de tiempo de recepción de datos. |\n| 30 | `total_clases_asistidas` | `integer` | Paso 2 | Total de clases asistidas en el período. |\n| 31 | `clases_por_tipo` | `object` | Paso 2 | Mapa de tipo de clase → cantidad. |\n| 32 | `clase_mas_frecuente` | `string` | Paso 2 | Tipo de clase con más asistencias. |\n| 33 | `instructor_mas_frecuente` | `string` | Paso 2 | Instructor con más clases impartidas al miembro. |\n| 34 | `horario_preferido` | `string` | Paso 2 | Franja horaria con más asistencias. |\n| 35 | `duracion_promedio_sesion` | `integer` | Paso 2 | Promedio de duración de las sesiones en minutos. |\n| 36 | `total_minutos_actividad` | `integer` | Paso 2 | Suma total de minutos de actividad. |\n| 37 | `racha_dias_seguidos` | `integer` | Paso 2 | Mayor racha de días consecutivos con asistencia. |\n| 38 | `primera_asistencia_periodo` | `string` | Paso 2 | Primera fecha de asistencia en el período. |\n| 39 | `ultima_asistencia_periodo` | `string` | Paso 2 | Última fecha de asistencia en el período. |\n| 40 | `dias_sin_asistencia` | `integer` | Paso 2 | Días del período sin registro de asistencia. |\n| 41 | `estado_miembro` | `string` | Paso 3 | Estado calculado del miembro (ej. \"Activo — Alta frecuencia\"). |\n| 42 | `fecha_hora_guardado` | `string` (ISO 8601) | Paso 4 | Marca de tiempo de guardado del registro. |\n| 43 | `fecha_hora_notificacion` | `string` (ISO 8601) o `null` | Paso 5 | Marca de tiempo de envío de la notificación. |\n\n---\n\n## Reglas de cálculo para campos derivados\n\n| Campo | Fórmula | Ejemplo |\n|---|---|---|\n| `clase_mas_frecuente` | Moda de `datos_clases[].clase` | 5 Spinning > 1 Yoga → `\"Spinning\"` |\n| `instructor_mas_frecuente` | Moda de `datos_clases[].instructor` | 5 Carlos > 1 Laura > 1 Ana → `\"Carlos Pérez\"` |\n| `horario_preferido` | Moda de `datos_checkin[].hora_entrada` agrupada por franja | 5 en 07:00–10:00 → `\"Mañana (07:00 – 10:00)\"` |\n| `duracion_promedio_sesion` | Promedio de `datos_checkin[].duracion_minutos` | (90+90+60+90+60+90+90)/7 = 84 |\n| `total_minutos_actividad` | Suma de `datos_checkin[].duracion_minutos` | 90+90+60+90+60+90+90 = 585 |\n| `racha_dias_seguidos` | Mayor secuencia de días consecutivos con check-in | 17→18→19→20→21→22→23 = 7 |\n| `dias_sin_asistencia` | (Días del período + 1) - Total de fechas únicas con check-in | (7+1) - 7 = 0 (si período es 17–23 agosto) |\n| `estado_miembro` | Ver tabla de estados abajo | 7 días/semana, 0 sin asistencia → `\"Activo — Alta frecuencia\"` |\n\n**Tabla de estados del miembro:**\n\n| Estado | Condición |\n|---|---|\n| `Activo — Alta frecuencia` | ≥5 días/semana con asistencia |\n| `Activo — Frecuencia moderada` | 3-4 días/semana con asistencia |\n| `Activo — Frecuencia baja` | 1-2 días/semana con asistencia |\n| `En riesgo — Sin asistencia reciente` | Última asistencia hace >7 días |\n| `Inactivo` | Sin asistencia en todo el período |\n\n---\n\n## Validaciones de entrada\n\n| Campo | Regla | Acción si falla |\n|---|---|---|\n| `evento_id` | No vacío, formato válido | Terminar flujo sin ejecutar |\n| `contacto_id` | Debe seguir formato `CT-YYYY-XXXXX` | Terminar flujo sin ejecutar |\n| `nombre_miembro` | No vacío, mínimo 2 caracteres | Terminar flujo sin ejecutar |\n| `periodo_inicio` | Formato YYYY-MM-DD, anterior o igual a `periodo_fin` | Terminar flujo sin ejecutar |\n| `periodo_fin` | Formato YYYY-MM-DD, igual o posterior a `periodo_inicio` | Terminar flujo sin ejecutar |\n| `gimnasio_id` | No vacío | Terminar flujo sin ejecutar |\n| `responsable_notificacion` | No vacío | Terminar flujo sin ejecutar |\n| `canal_notificacion` | Debe ser uno de los 4 valores válidos | Usar `email` como valor por defecto |\n| `email_responsable` | Requerido si `canal_notificacion` es `email` | Escalar a humano (C4.1) |\n| `telefono_responsable` | Requerido si `canal_notificacion` es `whatsapp` o `sms` | Escalar a humano (C4.1) |\n| `datos_checkin` | Si está presente, debe ser array con al menos 1 objeto válido | Tratar como array vacío |\n| `datos_clases` | Si está presente, debe ser array con al menos 1 objeto válido | Tratar como array vacío |\n| `notas_entrenador` | Si está presente, debe ser array con al menos 1 objeto válido | Tratar como array vacío |\n\n---\n\n## Ejemplo de payload completo\n\n```json\n{\n  \"evento_id\": \"REC-20260823-200000-001\",\n  \"contacto_id\": \"CT-2026-00452\",\n  \"tipo_generacion\": \"semanal\",\n  \"fecha_hora_solicitud\": \"2026-08-23T20:00:00Z\",\n  \"nombre_miembro\": \"María López\",\n  \"telefono_miembro\": \"+54 11 5555-1234\",\n  \"email_miembro\": \"maria.lopez@email.com\",\n  \"membresia_tipo\": \"premium\",\n  \"membresia_estado\": \"activa\",\n  \"datos_checkin\": [\n    {\"fecha\": \"2026-08-17\", \"hora_entrada\": \"08:15\", \"hora_salida\": \"09:45\", \"duracion_minutos\": 90, \"area\": \"Sala de spinning\"},\n    {\"fecha\": \"2026-08-18\", \"hora_entrada\": \"08:00\", \"hora_salida\": \"09:30\", \"duracion_minutos\": 90, \"area\": \"Sala de spinning\"},\n    {\"fecha\": \"2026-08-19\", \"hora_entrada\": \"17:00\", \"hora_salida\": \"18:00\", \"duracion_minutos\": 60, \"area\": \"Sala de yoga\"},\n    {\"fecha\": \"2026-08-20\", \"hora_entrada\": \"08:10\", \"hora_salida\": \"09:40\", \"duracion_minutos\": 90, \"area\": \"Sala de spinning\"},\n    {\"fecha\": \"2026-08-21\", \"hora_entrada\": \"07:30\", \"hora_salida\": \"08:30\", \"duracion_minutos\": 60, \"area\": \"Piso de gimnasio\"},\n    {\"fecha\": \"2026-08-22\", \"hora_entrada\": \"08:00\", \"hora_salida\": \"09:30\", \"duracion_minutos\": 90, \"area\": \"Sala de spinning\"},\n    {\"fecha\": \"2026-08-23\", \"hora_entrada\": \"08:05\", \"hora_salida\": \"09:35\", \"duracion_minutos\": 90, \"area\": \"Sala de spinning\"}\n  ],\n  \"datos_clases\": [\n    {\"fecha\": \"2026-08-17\", \"clase\": \"Spinning Matutino\", \"instructor\": \"Carlos Pérez\", \"sala\": \"Sala 3\", \"duracion_minutos\": 60},\n    {\"fecha\": \"2026-08-18\", \"clase\": \"Spinning Matutino\", \"instructor\": \"Carlos Pérez\", \"sala\": \"Sala 3\", \"duracion_minutos\": 60},\n    {\"fecha\": \"2026-08-19\", \"clase\": \"Yoga Relajación\", \"instructor\": \"Laura Gómez\", \"sala\": \"Sala 1\", \"duracion_minutos\": 60},\n    {\"fecha\": \"2026-08-20\", \"clase\": \"Spinning Matutino\", \"instructor\": \"Carlos Pérez\", \"sala\": \"Sala 3\", \"duracion_minutos\": 60},\n    {\"fecha\": \"2026-08-21\", \"clase\": \"Funcional Express\", \"instructor\": \"Ana Martínez\", \"sala\": \"Piso 2\", \"duracion_minutos\": 45},\n    {\"fecha\": \"2026-08-22\", \"clase\": \"Spinning Matutino\", \"instructor\": \"Carlos Pérez\", \"sala\": \"Sala 3\", \"duracion_minutos\": 60},\n    {\"fecha\": \"2026-08-23\", \"clase\": \"Spinning Matutino\", \"instructor\": \"Carlos Pérez\", \"sala\": \"Sala 3\", \"duracion_minutos\": 60}\n  ],\n  \"notas_entrenador\": [\n    {\"fecha\": \"2026-08-19\", \"instructor\": \"Laura Gómez\", \"nota\": \"María mejoró su flexibilidad en postura de paloma\"},\n    {\"fecha\": \"2026-08-23\", \"instructor\": \"Carlos Pérez\", \"nota\": \"María aumentó la resistencia, mantuvo ritmo alto todo el clase\"}\n  ],\n  \"datos_app\": {\n    \"logins_semana\": 5,\n    \"clases_consultadas\": 8,\n    \"perfil_vistas\": 2\n  },\n  \"datos_evaluacion\": null,\n  \"periodo_inicio\": \"2026-08-17\",\n  \"periodo_fin\": \"2026-08-23\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"gimnasio_telefono\": \"+54 11 4444-5678\",\n  \"responsable_notificacion\": \"Carlos Pérez (Entrenador principal)\",\n  \"canal_notificacion\": \"email\",\n  \"email_responsable\": \"carlos.perez@fitzone.com\",\n  \"telefono_responsable\": null,\n  \"tiempo_fecha_actual\": \"2026-08-23T20:00:00Z\",\n  \"hora_preferida_notificacion\": \"20:00\",\n  \"horario_atencion_inicio\": \"07:00\",\n  \"horario_atencion_fin\": \"22:00\"\n}\n```\n\n---\n*Plantilla generada para el nicho Gimnasios y Fitness — Agente Attendance Recap Agent*\n"
              }
            ]
          }
        ]
      },
      {
        "id": "onboarding-journey",
        "carpeta": "onboarding-journey-agent",
        "nombre": "Onboarding Journey Agent",
        "icono": "🤝",
        "descripcion": "Guiar al nuevo miembro a través de su primeras semanas en el gimnasio",
        "submodulos": [],
        "categoria": "general",
        "estructura": [
          {
            "id": "onboarding-journey-agent",
            "etiqueta": "",
            "es_plantilla": true,
            "nombre": "onboarding-journey-agent",
            "subcarpetas": [],
            "notas": [
              {
                "id": "onboarding-journey-agent/flujo.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "flujo.md",
                "contenido": "# Flujo de ejecución — Onboarding Journey Agent\n\nEste archivo documenta el flujo fijo de ejecución del agente. Cada proceso de onboarding sigue estos 5 pasos **en orden**, sin pasos condicionales ni bifurcaciones.\n\n---\n\n## Paso 1 — Detección del alta de un nuevo cliente/miembro en el sistema\n\nEl agente detecta que un nuevo miembro fue dado de alta en el sistema del gimnasio y activa el flujo de onboarding.\n\n**Acción del agente:**\n1. Recibir los datos del nuevo miembro según lo definido en `variables-entrada.md`.\n2. Registrar la marca de tiempo de detección en `fecha_hora_deteccion_alta`.\n3. Validar que el `contacto_id` tenga un formato válido.\n4. Verificar que el `estado_membresia` sea `activa`.\n5. Verificar que el miembro no tenga un onboarding previo activo o completado.\n6. Inicializar la checklist de onboarding con todos los pasos en estado `pendiente`.\n7. Calcular las fechas límite para cada paso según los plazos definidos.\n8. Determinar el canal de comunicación preferido del miembro.\n9. Generar el `onboarding_id` único.\n\n**Tabla de checklist inicializada:**\n\n| # | Paso | Estado | Fecha límite | Recordatorios enviados |\n|---|------|--------|-------------|----------------------|\n| 1 | Completar perfil | `pendiente` | Alta + 48h | 0 |\n| 2 | Activar método de pago | `pendiente` | Alta + 48h | 0 |\n| 3 | Reservar primera clase | `pendiente` | Alta + 72h | 0 |\n| 4 | Asistir a evaluación física | `pendiente` | Alta + 7 días | 0 |\n| 5 | Primera clase completada | `pendiente` | Alta + 14 días | 0 |\n\n**Ejemplo de evento detectado:**\n\n```json\n{\n  \"evento_id\": \"ONB-20260823-100000-001\",\n  \"contacto_id\": \"CT-2026-00701\",\n  \"nombre_miembro\": \"Lucía Martínez\",\n  \"telefono_miembro\": \"+54 11 7777-1234\",\n  \"email_miembro\": \"lucia.martinez@email.com\",\n  \"membresia_tipo\": \"premium\",\n  \"membresia_estado\": \"activa\",\n  \"membresia_fecha_inicio\": \"2026-08-23\",\n  \"canal_preferido\": \"whatsapp\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"gimnasio_telefono\": \"+54 11 4444-5678\",\n  \"enlace_perfil\": \"https://app.fitzone.com/perfil/completar\",\n  \"enlace_pago\": \"https://app.fitzone.com/pago/activar\",\n  \"enlace_clases\": \"https://app.fitzone.com/clases/reservar\",\n  \"enlace_evaluacion\": \"https://app.fitzone.com/evaluacion/agendar\",\n  \"responsable_notificacion\": \"Carlos Pérez (Entrenador principal)\",\n  \"canal_notificacion_equipo\": \"email\",\n  \"email_responsable\": \"carlos.perez@fitzone.com\",\n  \"tiempo_fecha_actual\": \"2026-08-23T10:00:00Z\",\n  \"horario_atencion_inicio\": \"07:00\",\n  \"horario_atencion_fin\": \"22:00\"\n}\n```\n\n---\n\n## Paso 2 — Envío de la secuencia fija de mensajes de bienvenida con los pasos a seguir\n\nEl agente envía la secuencia fija de mensajes de bienvenida al nuevo miembro, indicando los pasos que debe completar para incorporarse al gimnasio.\n\n**Acción del agente:**\n1. Generar el mensaje de bienvenida usando la plantilla de `formato-salida.md` → sección 1.\n2. Insertar los enlaces directos para cada paso.\n3. Enviar el mensaje por el `canal_preferido` del miembro.\n4. Si el envío falla, reintentar una sola vez después de 30 segundos.\n5. Si el segundo intento falla, registrar el error y activar escalamiento (ver `reglas-escalamiento.md` → C3.1).\n6. Registrar la marca de tiempo de envío en `fecha_hora_envio_bienvenida`.\n7. Actualizar el estado del caso a `bienvenida_enviada`.\n8. Registrar en la checklist que el paso 0 (bienvenida) fue completado.\n\n**Plantilla de mensaje de bienvenida (WhatsApp/SMS):**\n\n```\n¡Hola [nombre_miembro]! ¡Bienvenido a [gimnasio_nombre]! 🎉💪\n\nTu membresía está activa. Estos son los pasos para empezar a entrenar:\n\n1️⃣ Completá tu perfil en la app:\n[enlace_perfil]\n\n2️⃣ Activá tu método de pago:\n[enlace_pago]\n\n3️⃣ Reservá tu primera clase:\n[enlace_clases]\n\n4️⃣ Asistí a tu evaluación física inicial (gratuita):\n[enlace_evaluacion]\n\n¿Dudas? Escribinos por este canal.\n— [gimnasio_nombre]\n```\n\n**Plantilla de mensaje de bienvenida (email):**\n\n**Asunto:** ¡Bienvenido a [gimnasio_nombre]! Tu guía de inicio 🎉\n\n```\n¡Hola [nombre_miembro]!\n\n¡Bienvenido a [gimnasio_nombre]! 🎉💪 Estamos felices de que nos hayas elegido.\n\nTu membresía [membresia_tipo] está activa desde el [fecha_inicio_formateada].\n\nPara empezar a entrenar, completá estos pasos:\n\n1️⃣ Completá tu perfil en la app\n   👉 [enlace_perfil]\n   Necesitamos tus datos para personalizar tu experiencia.\n\n2️⃣ Activá tu método de pago\n   👉 [enlace_pago]\n   Solo toma 1 minuto. Aceptamos tarjetas, débito y transferencia.\n\n3️⃣ Reservá tu primera clase\n   👉 [enlace_clases]\n   Elegí entre Spinning, Yoga, Fuerza, Boxeo y más.\n\n4️⃣ Asistí a tu evaluación física inicial (gratuita)\n   👉 [enlace_evaluacion]\n   Un entrenador te evaluará para diseñar tu plan personalizado.\n\n¿Tenés dudas? Respondé a este email o escribinos por WhatsApp al [gimnasio_telefono].\n\n¡Te esperamos en el gym! 💪\n— El equipo de [gimnasio_nombre]\n```\n\n**Ejemplo renderizado (WhatsApp):**\n\n```\n¡Hola Lucía Martínez! ¡Bienvenido a FitZone Centro! 🎉💪\n\nTu membresía está activa. Estos son los pasos para empezar a entrenar:\n\n1️⃣ Completá tu perfil en la app:\nhttps://app.fitzone.com/perfil/completar\n\n2️⃣ Activá tu método de pago:\nhttps://app.fitzone.com/pago/activar\n\n3️⃣ Reservá tu primera clase:\nhttps://app.fitzone.com/clases/reservar\n\n4️⃣ Asistí a tu evaluación física inicial (gratuita):\nhttps://app.fitzone.com/evaluacion/agendar\n\n¿Dudas? Escribinos por este canal.\n— FitZone Centro\n```\n\n**Ejemplo renderizado (email):**\n\n```\nAsunto: ¡Bienvenido a FitZone Centro! Tu guía de inicio 🎉\n\n¡Hola Lucía Martínez!\n\n¡Bienvenido a FitZone Centro! 🎉💪 Estamos felices de que nos hayas elegido.\n\nTu membresía premium está activa desde el 23 de agosto de 2026.\n\nPara empezar a entrenar, completá estos pasos:\n\n1️⃣ Completá tu perfil en la app\n   👉 https://app.fitzone.com/perfil/completar\n   Necesitamos tus datos para personalizar tu experiencia.\n\n2️⃣ Activá tu método de pago\n   👉 https://app.fitzone.com/pago/activar\n   Solo toma 1 minuto. Aceptamos tarjetas, débito y transferencia.\n\n3️⃣ Reservá tu primera clase\n   👉 https://app.fitzone.com/clases/reservar\n   Elegí entre Spinning, Yoga, Fuerza, Boxeo y más.\n\n4️⃣ Asistí a tu evaluación física inicial (gratuita)\n   👉 https://app.fitzone.com/evaluacion/agendar\n   Un entrenador te evaluará para diseñar tu plan personalizado.\n\n¿Tenés dudas? Respondé a este email o escribinos por WhatsApp al +54 11 4444-5678.\n\n¡Te esperamos en el gym! 💪\n— El equipo de FitZone Centro\n```\n\n---\n\n## Paso 3 — Verificación del cumplimiento de cada paso contra la checklist\n\nEl agente verifica periódicamente el estado de cada paso de la checklist contra el sistema del gimnasio.\n\n**Acción del agente:**\n1. Consultar el estado de cada paso en el sistema:\n   - **Perfil completado:** Verificar si el formulario de perfil fue enviado con todos los campos obligatorios.\n   - **Método de pago activado:** Verificar si hay un método de pago registrado y activo.\n   - **Primera clase reservada:** Verificar si hay al menos una reserva de clase en el calendario.\n   - **Evaluación física asistida:** Verificar si hay un registro de evaluación física completada.\n   - **Primera clase completada:** Verificar si hay al menos una clase con estado `completada` en el historial.\n2. Actualizar el estado de cada paso en la checklist.\n3. Si un paso cambia de `pendiente` a `completado`:\n   - Registrar la fecha y hora de cumplimiento.\n   - Enviar mensaje de confirmación al miembro (ver `formato-salida.md` → sección 2).\n4. Si un paso sigue `pendiente` y su fecha límite se acerca, proceder al paso 4.\n5. Si todos los pasos están `completados`, proceder al paso 5.\n\n**Tabla de verificación por paso:**\n\n| Paso | Campo del sistema a verificar | Condición de cumplimiento |\n|------|------------------------------|--------------------------|\n| Completar perfil | `perfil_estado` | `completado` y todos los campos obligatorios con valor |\n| Activar método de pago | `metodo_pago_estado` | `activo` con datos válidos |\n| Reservar primera clase | `reservas_clases` | Al menos 1 reserva con estado `confirmada` |\n| Asistir a evaluación física | `evaluacion_estado` | `completada` con fecha de asistencia |\n| Primera clase completada | `historial_clases` | Al menos 1 clase con estado `completada` |\n\n**Ejemplo de verificación (paso 1 completado):**\n\n```json\n{\n  \"paso_verificado\": \"completar_perfil\",\n  \"estado_anterior\": \"pendiente\",\n  \"estado_nuevo\": \"completado\",\n  \"fecha_cumplimiento\": \"2026-08-23T14:30:00Z\",\n  \"campos_completados\": [\"nombre\", \"apellido\", \"telefono\", \"email\", \"fecha_nacimiento\", \"objetivo_fitness\"],\n  \"checklist_actualizada\": [\n    {\"paso\": \"completar_perfil\", \"estado\": \"completado\", \"fecha_cumplimiento\": \"2026-08-23T14:30:00Z\"},\n    {\"paso\": \"activar_metodo_pago\", \"estado\": \"pendiente\", \"fecha_limite\": \"2026-08-25T10:00:00Z\"},\n    {\"paso\": \"reservar_primera_clase\", \"estado\": \"pendiente\", \"fecha_limite\": \"2026-08-26T10:00:00Z\"},\n    {\"paso\": \"asistir_evaluacion_fisica\", \"estado\": \"pendiente\", \"fecha_limite\": \"2026-08-30T10:00:00Z\"},\n    {\"paso\": \"primera_clase_completada\", \"estado\": \"pendiente\", \"fecha_limite\": \"2026-09-06T10:00:00Z\"}\n  ]\n}\n```\n\n**Ejemplo de verificación (paso 2 pendiente, fecha límite próxima):**\n\n```json\n{\n  \"paso_verificado\": \"activar_metodo_pago\",\n  \"estado_anterior\": \"pendiente\",\n  \"estado_nuevo\": \"pendiente\",\n  \"fecha_limite\": \"2026-08-25T10:00:00Z\",\n  \"horas_restantes\": 36,\n  \"recordatorios_enviados\": 0,\n  \"accion\": \"enviar_primer_recordatorio\"\n}\n```\n\n---\n\n## Paso 4 — Envío de recordatorios automáticos para los pasos pendientes\n\nEl agente envía recordatorios automáticos cuando un paso está pendiente y se acerca su fecha límite.\n\n**Acción del agente:**\n1. Para cada paso con estado `pendiente`:\n   - Verificar cuántos recordatorios se han enviado (`recordatorios_enviados`).\n   - Si es menor a 3, verificar si corresponde enviar un recordatorio según la tabla de intervalos.\n   - Si corresponde, generar el mensaje de recordatorio usando la plantilla de `formato-salida.md` → sección 3.\n   - Enviar el recordatorio por el `canal_preferido`.\n   - Incrementar el contador `recordatorios_enviados`.\n   - Registrar la marca de tiempo de envío en `fecha_hora_ultimo_recordatorio`.\n2. Si se alcanzó el tercer recordatorio y el paso sigue pendiente:\n   - Escalar a humano (ver `reglas-escalamiento.md` → C1.1).\n3. Si el paso se completa después de un recordatorio, enviar mensaje de confirmación.\n\n**Plantilla de recordatorio (WhatsApp/SMS) — Paso 1 (Perfil):**\n\n```\n¡Hola [nombre_miembro]! 📋\n\n¿Ya completaste tu perfil en [gimnasio_nombre]? Solo toma 2 minutos:\n\n👉 [enlace_perfil]\n\nAsí podemos personalizar tu experiencia de entrenamiento.\n— [gimnasio_nombre]\n```\n\n**Plantilla de recordatorio (WhatsApp/SMS) — Paso 2 (Pago):**\n\n```\n¡Hola [nombre_miembro]! 💳\n\nPara disfrutar tu membresía al full, activá tu método de pago:\n\n👉 [enlace_pago]\n\nAceptamos tarjetas, débito y transferencia.\n— [gimnasio_nombre]\n```\n\n**Plantilla de recordatorio (WhatsApp/SMS) — Paso 3 (Reserva):**\n\n```\n¡Hola [nombre_miembro]! 🏋️\n\n¿Ya reservaste tu primera clase? Tenemos Spinning, Yoga, Fuerza, Boxeo y más:\n\n👉 [enlace_clases]\n\nElegí el horario que mejor te quede.\n— [gimnasio_nombre]\n```\n\n**Plantilla de recordatorio (WhatsApp/SMS) — Paso 4 (Evaluación):**\n\n```\n¡Hola [nombre_miembro]! 📊\n\nTu evaluación física inicial es gratuita y nos ayuda a diseñar tu plan personalizado:\n\n👉 [enlace_evaluacion]\n\nAgendala cuando quieras, ¡es rápido!\n— [gimnasio_nombre]\n```\n\n**Plantilla de recordatorio (WhatsApp/SMS) — Paso 5 (Primera clase):**\n\n```\n¡Hola [nombre_miembro]! 💪\n\nYa es hora de entrenar. ¿Ya asististe a tu primera clase?\n\nSi ainda no reservaste, hacelo acá:\n👉 [enlace_clases]\n\n¡Te esperamos!\n— [gimnasio_nombre]\n```\n\n**Plantilla de recordatorio final (tercer recordatorio — escalación):**\n\n```\n¡Hola [nombre_miembro]! 👋\n\nParece que necesitás una mano para completar tu incorporación. Un miembro de nuestro equipo te va a contactar para ayudarte personalmente.\n\nMientras tanto, si tenés alguna duda, escribinos por este canal.\n— [gimnasio_nombre]\n```\n\n**Ejemplo renderizado de recordatorio (paso 1 - WhatsApp):**\n\n```\n¡Hola Lucía Martínez! 📋\n\n¿Ya completaste tu perfil en FitZone Centro? Solo toma 2 minutos:\n\n👉 https://app.fitzone.com/perfil/completar\n\nAsí podemos personalizar tu experiencia de entrenamiento.\n— FitZone Centro\n```\n\n**Ejemplo renderizado de recordatorio final (WhatsApp):**\n\n```\n¡Hola Lucía Martínez! 👋\n\nParece que necesitás una mano para completar tu incorporación. Un miembro de nuestro equipo te va a contactar para ayudarte personalmente.\n\nMientras tanto, si tenés alguna duda, escribinos por este canal.\n— FitZone Centro\n```\n\n---\n\n## Paso 5 — Marcado del onboarding como completo y notificación al responsable humano\n\nCuando todos los pasos de la checklist están en estado `completado`, el agente marca el onboarding como completo y notifica al responsable humano.\n\n**Acción del agente:**\n1. Verificar que todos los pasos de la checklist estén en estado `completado`.\n2. Calcular la duración total del onboarding (`fecha_hora_completado` - `fecha_hora_deteccion_alta`).\n3. Actualizar el estado del onboarding a `completado`.\n4. Enviar mensaje de felicitación al miembro (ver `formato-salida.md` → sección 4).\n5. Enviar notificación al responsable humano (ver `formato-salida.md` → sección 5).\n6. Actualizar el perfil del miembro con `onboarding_estado: completado`.\n7. Registrar la marca de tiempo de cierre en `fecha_hora_completado`.\n8. Generar el registro de resultado final.\n\n**Tabla de estados del onboarding:**\n\n| Estado | Condición |\n|---|---|\n| `en_progreso` | Al menos 1 paso pendiente, plazos vigentes |\n| `completado` | Todos los pasos en estado `completado` |\n| `cancelado` | El miembro solicitó cancelar |\n| `escorado` | El caso fue transferido a humano |\n| `vencido` | Todos los plazos expiraron sin completar todos los pasos |\n\n**Plantilla de felicitación al miembro (WhatsApp/SMS):**\n\n```\n¡[nombre_miembro], felicitaciones! 🎉\n\nCompletaste todos los pasos de tu incorporación en [gimnasio_nombre].\n\n✅ Perfil completado\n✅ Método de pago activado\n✅ Primera clase reservada\n✅ Evaluación física realizada\n✅ Primera clase completada\n\nYa estás listo para entrenar al máximo. ¡Nos vemos en el gym! 💪\n— [gimnasio_nombre]\n```\n\n**Plantilla de felicitación al miembro (email):**\n\n**Asunto:** ¡Felicidades, [nombre_miembro]! Completaste tu incorporación 🎉\n\n```\n¡Hola [nombre_miembro]!\n\n¡Felicitaciones! 🎉 Completaste todos los pasos de tu incorporación en [gimnasio_nombre].\n\nTu checklist de onboarding:\n\n✅ Perfil completado — [fecha_perfil]\n✅ Método de pago activado — [fecha_pago]\n✅ Primera clase reservada — [fecha_reserva]\n✅ Evaluación física realizada — [fecha_evaluacion]\n✅ Primera clase completada — [fecha_clase]\n\n📊 Resumen:\n- Tiempo total de incorporación: [duracion_onboarding]\n- Pasos completados: 5/5\n- Membresía: [membresia_tipo]\n\nYa estás listo para entrenar al máximo. Si tenés alguna pregunta o querés optimizar tu rutina, escribinos o hablá con tu entrenador.\n\n¡Nos vemos en el gym! 💪\n— El equipo de [gimnasio_nombre]\n```\n\n**Plantilla de notificación al responsable humano:**\n\n**Asunto:** Onboarding completado — [nombre_miembro] | [gimnasio_nombre]\n\n```\nHola [responsable_nombre],\n\nEl onboarding de [nombre_miembro] se completó exitosamente.\n\n👤 Miembro: [nombre_miembro] ([contacto_id])\n📋 Membresía: [membresia_tipo]\n📅 Alta: [fecha_alta]\n⏱️ Duración del onboarding: [duracion_onboarding]\n\n✅ Checklist completada:\n- Perfil completado: [fecha_perfil]\n- Método de pago activado: [fecha_pago]\n- Primera clase reservada: [fecha_reserva]\n- Evaluación física realizada: [fecha_evaluacion]\n- Primera clase completada: [fecha_clase]\n\nRegistro: [onboarding_id]\n— [gimnasio_nombre]\n```\n\n**Ejemplo renderizado de felicitación (WhatsApp):**\n\n```\n¡Lucía Martínez, felicitaciones! 🎉\n\nCompletaste todos los pasos de tu incorporación en FitZone Centro.\n\n✅ Perfil completado\n✅ Método de pago activado\n✅ Primera clase reservada\n✅ Evaluación física realizada\n✅ Primera clase completada\n\nYa estás listo para entrenar al máximo. ¡Nos vemos en el gym! 💪\n— FitZone Centro\n```\n\n**Ejemplo renderizado de notificación al responsable (email):**\n\n```\nAsunto: Onboarding completado — Lucía Martínez | FitZone Centro\n\nHola Carlos Pérez,\n\nEl onboarding de Lucía Martínez se completó exitosamente.\n\n👤 Miembro: Lucía Martínez (CT-2026-00701)\n📋 Membresía: Premium\n📅 Alta: 23/08/2026\n⏱️ Duración del onboarding: 5 días, 6 horas\n\n✅ Checklist completada:\n- Perfil completado: 23/08/2026 14:30\n- Método de pago activado: 23/08/2026 15:00\n- Primera clase reservada: 24/08/2026 09:00\n- Evaluación física realizada: 25/08/2026 10:00\n- Primera clase completada: 27/08/2026 09:00\n\nRegistro: ONB-20260823-100000-001\n— FitZone Centro\n```\n\n---\n*Plantilla generada para el nicho Gimnasios y Fitness — Agente Onboarding Journey Agent*\n"
              },
              {
                "id": "onboarding-journey-agent/formato-salida.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "formato-salida.md",
                "contenido": "# Formato de salida — Onboarding Journey Agent\n\n## Descripción\n\nDefine la estructura exacta del mensaje o registro que el agente debe producir en cada paso del flujo. Todos los campos deben tener valores concretos de ejemplo.\n\n---\n\n## 1. Mensaje de bienvenida\n\n**Cuándo se genera:** Paso 2 — Cuando se detecta el alta del nuevo miembro.\n\n**Plantilla (WhatsApp/SMS):**\n\n```\n¡Hola [nombre_miembro]! ¡Bienvenido a [gimnasio_nombre]! 🎉💪\n\nTu membresía está activa. Estos son los pasos para empezar a entrenar:\n\n1️⃣ Completá tu perfil en la app:\n[enlace_perfil]\n\n2️⃣ Activá tu método de pago:\n[enlace_pago]\n\n3️⃣ Reservá tu primera clase:\n[enlace_clases]\n\n4️⃣ Asistí a tu evaluación física inicial (gratuita):\n[enlace_evaluacion]\n\n¿Dudas? Escribinos por este canal.\n— [gimnasio_nombre]\n```\n\n**Ejemplo renderizado (WhatsApp):**\n\n```\n¡Hola Lucía Martínez! ¡Bienvenido a FitZone Centro! 🎉💪\n\nTu membresía está activa. Estos son los pasos para empezar a entrenar:\n\n1️⃣ Completá tu perfil en la app:\nhttps://app.fitzone.com/perfil/completar\n\n2️⃣ Activá tu método de pago:\nhttps://app.fitzone.com/pago/activar\n\n3️⃣ Reservá tu primera clase:\nhttps://app.fitzone.com/clases/reservar\n\n4️⃣ Asistí a tu evaluación física inicial (gratuita):\nhttps://app.fitzone.com/evaluacion/agendar\n\n¿Dudas? Escribinos por este canal.\n— FitZone Centro\n```\n\n**Plantilla (email):**\n\n**Asunto:** ¡Bienvenido a [gimnasio_nombre]! Tu guía de inicio 🎉\n\n```\n¡Hola [nombre_miembro]!\n\n¡Bienvenido a [gimnasio_nombre]! 🎉💪 Estamos felices de que nos hayas elegido.\n\nTu membresía [membresia_tipo] está activa desde el [fecha_inicio_formateada].\n\nPara empezar a entrenar, completá estos pasos:\n\n1️⃣ Completá tu perfil en la app\n   👉 [enlace_perfil]\n   Necesitamos tus datos para personalizar tu experiencia.\n\n2️⃣ Activá tu método de pago\n   👉 [enlace_pago]\n   Solo toma 1 minuto. Aceptamos tarjetas, débito y transferencia.\n\n3️⃣ Reservá tu primera clase\n   👉 [enlace_clases]\n   Elegí entre Spinning, Yoga, Fuerza, Boxeo y más.\n\n4️⃣ Asistí a tu evaluación física inicial (gratuita)\n   👉 [enlace_evaluacion]\n   Un entrenador te evaluará para diseñar tu plan personalizado.\n\n¿Tenés dudas? Respondé a este email o escribinos por WhatsApp al [gimnasio_telefono].\n\n¡Te esperamos en el gym! 💪\n— El equipo de [gimnasio_nombre]\n```\n\n**Ejemplo renderizado (email):**\n\n```\nAsunto: ¡Bienvenido a FitZone Centro! Tu guía de inicio 🎉\n\n¡Hola Lucía Martínez!\n\n¡Bienvenido a FitZone Centro! 🎉💪 Estamos felices de que nos hayas elegido.\n\nTu membresía premium está activa desde el 23 de agosto de 2026.\n\nPara empezar a entrenar, completá estos pasos:\n\n1️⃣ Completá tu perfil en la app\n   👉 https://app.fitzone.com/perfil/completar\n   Necesitamos tus datos para personalizar tu experiencia.\n\n2️⃣ Activá tu método de pago\n   👉 https://app.fitzone.com/pago/activar\n   Solo toma 1 minuto. Aceptamos tarjetas, débito y transferencia.\n\n3️⃣ Reservá tu primera clase\n   👉 https://app.fitzone.com/clases/reservar\n   Elegí entre Spinning, Yoga, Fuerza, Boxeo y más.\n\n4️⃣ Asistí a tu evaluación física inicial (gratuita)\n   👉 https://app.fitzone.com/evaluacion/agendar\n   Un entrenador te evaluará para diseñar tu plan personalizado.\n\n¿Tenés dudas? Respondé a este email o escribinos por WhatsApp al +54 11 4444-5678.\n\n¡Te esperamos en el gym! 💪\n— El equipo de FitZone Centro\n```\n\n---\n\n## 2. Confirmación de paso completado\n\n**Cuándo se genera:** Paso 3 — Cuando el agente detecta que un paso fue completado.\n\n**Plantilla (WhatsApp/SMS):**\n\n```\n✅ ¡[paso_nombre] completado!\n\nBien hecho, [nombre_miembro]. Ya completaste [pasos_completados] de 5 pasos.\n\n[checklist_estado_visual]\n\n¿Necesitás ayuda con el siguiente paso? Escribinos.\n— [gimnasio_nombre]\n```\n\n**Ejemplo de checklist visual:**\n\n```\n✅ Perfil completado\n✅ Método de pago activado\n⬜ Reservar primera clase\n⬜ Evaluación física\n⬜ Primera clase\n```\n\n**Ejemplo renderizado (paso 2 completado):**\n\n```\n✅ ¡Método de pago activado!\n\nBien hecho, Lucía Martínez. Ya completaste 2 de 5 pasos.\n\n✅ Perfil completado\n✅ Método de pago activado\n⬜ Reservar primera clase\n⬜ Evaluación física\n⬜ Primera clase\n\n¿Necesitás ayuda con el siguiente paso? Escribinos.\n— FitZone Centro\n```\n\n---\n\n## 3. Recordatorio de paso pendiente\n\n**Cuándo se genera:** Paso 4 — Cuando un paso está pendiente y corresponde enviar recordatorio.\n\n**Plantilla por paso (WhatsApp/SMS):**\n\n**Paso 1 — Completar perfil:**\n\n```\n¡Hola [nombre_miembro]! 📋\n\n¿Ya completaste tu perfil en [gimnasio_nombre]? Solo toma 2 minutos:\n\n👉 [enlace_perfil]\n\nAsí podemos personalizar tu experiencia de entrenamiento.\n— [gimnasio_nombre]\n```\n\n**Paso 2 — Activar método de pago:**\n\n```\n¡Hola [nombre_miembro]! 💳\n\nPara disfrutar tu membresía al full, activá tu método de pago:\n\n👉 [enlace_pago]\n\nAceptamos tarjetas, débito y transferencia.\n— [gimnasio_nombre]\n```\n\n**Paso 3 — Reservar primera clase:**\n\n```\n¡Hola [nombre_miembro]! 🏋️\n\n¿Ya reservaste tu primera clase? Tenemos Spinning, Yoga, Fuerza, Boxeo y más:\n\n👉 [enlace_clases]\n\nElegí el horario que mejor te quede.\n— [gimnasio_nombre]\n```\n\n**Paso 4 — Asistir a evaluación física:**\n\n```\n¡Hola [nombre_miembro]! 📊\n\nTu evaluación física inicial es gratuita y nos ayuda a diseñar tu plan personalizado:\n\n👉 [enlace_evaluacion]\n\nAgendala cuando quieras, ¡es rápido!\n— [gimnasio_nombre]\n```\n\n**Paso 5 — Primera clase completada:**\n\n```\n¡Hola [nombre_miembro]! 💪\n\nYa es hora de entrenar. ¿Ya asististe a tu primera clase?\n\nSi ainda no reservaste, hacelo acá:\n👉 [enlace_clases]\n\n¡Te esperamos!\n— [gimnasio_nombre]\n```\n\n**Recordatorio final (tercer recordatorio — escalación):**\n\n```\n¡Hola [nombre_miembro]! 👋\n\nParece que necesitás una mano para completar tu incorporación. Un miembro de nuestro equipo te va a contactar para ayudarte personalmente.\n\nMientras tanto, si tenés alguna duda, escribinos por este canal.\n— [gimnasio_nombre]\n```\n\n**Ejemplo renderizado de recordatorio (paso 1):**\n\n```\n¡Hola Lucía Martínez! 📋\n\n¿Ya completaste tu perfil en FitZone Centro? Solo toma 2 minutos:\n\n👉 https://app.fitzone.com/perfil/completar\n\nAsí podemos personalizar tu experiencia de entrenamiento.\n— FitZone Centro\n```\n\n**Ejemplo renderizado de recordatorio final:**\n\n```\n¡Hola Lucía Martínez! 👋\n\nParece que necesitás una mano para completar tu incorporación. Un miembro de nuestro equipo te va a contactar para ayudarte personalmente.\n\nMientras tanto, si tenés alguna duda, escribinos por este canal.\n— FitZone Centro\n```\n\n---\n\n## 4. Felicitación de onboarding completado\n\n**Cuándo se genera:** Paso 5 — Cuando todos los pasos están completados.\n\n**Plantilla (WhatsApp/SMS):**\n\n```\n¡[nombre_miembro], felicitaciones! 🎉\n\nCompletaste todos los pasos de tu incorporación en [gimnasio_nombre].\n\n✅ Perfil completado\n✅ Método de pago activado\n✅ Primera clase reservada\n✅ Evaluación física realizada\n✅ Primera clase completada\n\nYa estás listo para entrenar al máximo. ¡Nos vemos en el gym! 💪\n— [gimnasio_nombre]\n```\n\n**Plantilla (email):**\n\n**Asunto:** ¡Felicidades, [nombre_miembro]! Completaste tu incorporación 🎉\n\n```\n¡Hola [nombre_miembro]!\n\n¡Felicitaciones! 🎉 Completaste todos los pasos de tu incorporación en [gimnasio_nombre].\n\nTu checklist de onboarding:\n\n✅ Perfil completado — [fecha_perfil]\n✅ Método de pago activado — [fecha_pago]\n✅ Primera clase reservada — [fecha_reserva]\n✅ Evaluación física realizada — [fecha_evaluacion]\n✅ Primera clase completada — [fecha_clase]\n\n📊 Resumen:\n- Tiempo total de incorporación: [duracion_onboarding]\n- Pasos completados: 5/5\n- Membresía: [membresia_tipo]\n\nYa estás listo para entrenar al máximo. Si tenés alguna pregunta o querés optimizar tu rutina, escribinos o hablá con tu entrenador.\n\n¡Nos vemos en el gym! 💪\n— El equipo de [gimnasio_nombre]\n```\n\n**Ejemplo renderizado (WhatsApp):**\n\n```\n¡Lucía Martínez, felicitaciones! 🎉\n\nCompletaste todos los pasos de tu incorporación en FitZone Centro.\n\n✅ Perfil completado\n✅ Método de pago activado\n✅ Primera clase reservada\n✅ Evaluación física realizada\n✅ Primera clase completada\n\nYa estás listo para entrenar al máximo. ¡Nos vemos en el gym! 💪\n— FitZone Centro\n```\n\n**Ejemplo renderizado (email):**\n\n```\nAsunto: ¡Felicidades, Lucía Martínez! Completaste tu incorporación 🎉\n\n¡Hola Lucía Martínez!\n\n¡Felicitaciones! 🎉 Completaste todos los pasos de tu incorporación en FitZone Centro.\n\nTu checklist de onboarding:\n\n✅ Perfil completado — 23/08/2026 14:30\n✅ Método de pago activado — 23/08/2026 15:00\n✅ Primera clase reservada — 24/08/2026 09:00\n✅ Evaluación física realizada — 25/08/2026 10:00\n✅ Primera clase completada — 27/08/2026 09:00\n\n📊 Resumen:\n- Tiempo total de incorporación: 4 días, 23 horas\n- Pasos completados: 5/5\n- Membresía: Premium\n\nYa estás listo para entrenar al máximo. Si tenés alguna pregunta o querés optimizar tu rutina, escribinos o hablá con tu entrenador.\n\n¡Nos vemos en el gym! 💪\n— El equipo de FitZone Centro\n```\n\n---\n\n## 5. Notificación al responsable humano — Onboarding completado\n\n**Cuándo se genera:** Paso 5 — Cuando el onboarding se marca como completo.\n\n**Notificación por email:**\n\n**Asunto:** Onboarding completado — [nombre_miembro] | [gimnasio_nombre]\n\n```\nHola [responsable_nombre],\n\nEl onboarding de [nombre_miembro] se completó exitosamente.\n\n👤 Miembro: [nombre_miembro] ([contacto_id])\n📋 Membresía: [membresia_tipo]\n📅 Alta: [fecha_alta]\n⏱️ Duración del onboarding: [duracion_onboarding]\n\n✅ Checklist completada:\n- Perfil completado: [fecha_perfil]\n- Método de pago activado: [fecha_pago]\n- Primera clase reservada: [fecha_reserva]\n- Evaluación física realizada: [fecha_evaluacion]\n- Primera clase completada: [fecha_clase]\n\nRegistro: [onboarding_id]\n— [gimnasio_nombre]\n```\n\n**Ejemplo renderizado:**\n\n```\nAsunto: Onboarding completado — Lucía Martínez | FitZone Centro\n\nHola Carlos Pérez,\n\nEl onboarding de Lucía Martínez se completó exitosamente.\n\n👤 Miembro: Lucía Martínez (CT-2026-00701)\n📋 Membresía: Premium\n📅 Alta: 23/08/2026\n⏱️ Duración del onboarding: 4 días, 23 horas\n\n✅ Checklist completada:\n- Perfil completado: 23/08/2026 14:30\n- Método de pago activado: 23/08/2026 15:00\n- Primera clase reservada: 24/08/2026 09:00\n- Evaluación física realizada: 25/08/2026 10:00\n- Primera clase completada: 27/08/2026 09:00\n\nRegistro: ONB-20260823-100000-001\n— FitZone Centro\n```\n\n---\n\n## 6. Registro de acción (JSON)\n\n**Cuándo se genera:** En cada paso del flujo, como registro interno.\n\n**Estructura:**\n\n```json\n{\n  \"registro_id\": \"ONB-REG-[timestamp]\",\n  \"agente\": \"onboarding-journey-agent\",\n  \"onboarding_id\": \"[onboarding_id]\",\n  \"contacto_id\": \"[contacto_id]\",\n  \"nombre_miembro\": \"[nombre_miembro]\",\n  \"gimnasio_id\": \"[gimnasio_id]\",\n  \"gimnasio_nombre\": \"[gimnasio_nombre]\",\n  \"evento_tipo\": \"[evento_tipo]\",\n  \"fecha_hora_alta\": \"[fecha_hora_alta]\",\n  \"membresia_tipo\": \"[membresia_tipo]\",\n  \"onboarding_estado\": \"[onboarding_estado]\",\n  \"pasos_completados\": \"[pasos_completados]\",\n  \"pasos_pendientes\": \"[pasos_pendientes]\",\n  \"porcentaje_completado\": \"[porcentaje_completado]\",\n  \"checklist_estado\": \"[checklist_estado_array]\",\n  \"fecha_hora_envio_bienvenida\": \"[fecha_hora_envio_bienvenida]\",\n  \"fecha_hora_completado\": \"[fecha_hora_completado]\",\n  \"duracion_onboarding_horas\": \"[duracion_onboarding_horas]\",\n  \"resultado\": \"[resultado]\",\n  \"timestamp_registro\": \"[tiempo_fecha_actual]\"\n}\n```\n\n**Ejemplo de registro completo (onboarding completado):**\n\n```json\n{\n  \"registro_id\": \"ONB-REG-20260827090000\",\n  \"agente\": \"onboarding-journey-agent\",\n  \"onboarding_id\": \"ONB-ID-20260823-100000-001\",\n  \"contacto_id\": \"CT-2026-00701\",\n  \"nombre_miembro\": \"Lucía Martínez\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"evento_tipo\": \"alta_nueva\",\n  \"fecha_hora_alta\": \"2026-08-23T10:00:00Z\",\n  \"membresia_tipo\": \"premium\",\n  \"onboarding_estado\": \"completado\",\n  \"pasos_completados\": 5,\n  \"pasos_pendientes\": 0,\n  \"porcentaje_completado\": 100,\n  \"checklist_estado\": [\n    {\"paso_id\": \"completar_perfil\", \"estado\": \"completado\", \"fecha_cumplimiento\": \"2026-08-23T14:30:00Z\", \"recordatorios_enviados\": 0},\n    {\"paso_id\": \"activar_metodo_pago\", \"estado\": \"completado\", \"fecha_cumplimiento\": \"2026-08-23T15:00:00Z\", \"recordatorios_enviados\": 0},\n    {\"paso_id\": \"reservar_primera_clase\", \"estado\": \"completado\", \"fecha_cumplimiento\": \"2026-08-24T09:00:00Z\", \"recordatorios_enviados\": 0},\n    {\"paso_id\": \"asistir_evaluacion_fisica\", \"estado\": \"completado\", \"fecha_cumplimiento\": \"2026-08-25T10:00:00Z\", \"recordatorios_enviados\": 0},\n    {\"paso_id\": \"primera_clase_completada\", \"estado\": \"completado\", \"fecha_cumplimiento\": \"2026-08-27T09:00:00Z\", \"recordatorios_enviados\": 0}\n  ],\n  \"fecha_hora_envio_bienvenida\": \"2026-08-23T10:00:05Z\",\n  \"fecha_hora_completado\": \"2026-08-27T09:00:00Z\",\n  \"duracion_onboarding_horas\": 95.0,\n  \"resultado\": \"completado\",\n  \"timestamp_registro\": \"2026-08-27T09:00:05Z\"\n}\n```\n\n**Ejemplo de registro (onboarding en progreso):**\n\n```json\n{\n  \"registro_id\": \"ONB-REG-20260824100000\",\n  \"agente\": \"onboarding-journey-agent\",\n  \"onboarding_id\": \"ONB-ID-20260823-100000-001\",\n  \"contacto_id\": \"CT-2026-00701\",\n  \"nombre_miembro\": \"Lucía Martínez\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"evento_tipo\": \"alta_nueva\",\n  \"fecha_hora_alta\": \"2026-08-23T10:00:00Z\",\n  \"membresia_tipo\": \"premium\",\n  \"onboarding_estado\": \"en_progreso\",\n  \"pasos_completados\": 2,\n  \"pasos_pendientes\": 3,\n  \"porcentaje_completado\": 40,\n  \"checklist_estado\": [\n    {\"paso_id\": \"completar_perfil\", \"estado\": \"completado\", \"fecha_cumplimiento\": \"2026-08-23T14:30:00Z\", \"recordatorios_enviados\": 0},\n    {\"paso_id\": \"activar_metodo_pago\", \"estado\": \"completado\", \"fecha_cumplimiento\": \"2026-08-23T15:00:00Z\", \"recordatorios_enviados\": 0},\n    {\"paso_id\": \"reservar_primera_clase\", \"estado\": \"pendiente\", \"fecha_cumplimiento\": null, \"recordatorios_enviados\": 1},\n    {\"paso_id\": \"asistir_evaluacion_fisica\", \"estado\": \"pendiente\", \"fecha_cumplimiento\": null, \"recordatorios_enviados\": 0},\n    {\"paso_id\": \"primera_clase_completada\", \"estado\": \"pendiente\", \"fecha_cumplimiento\": null, \"recordatorios_enviados\": 0}\n  ],\n  \"fecha_hora_envio_bienvenida\": \"2026-08-23T10:00:05Z\",\n  \"fecha_hora_completado\": null,\n  \"duracion_onboarding_horas\": null,\n  \"resultado\": \"en_progreso\",\n  \"timestamp_registro\": \"2026-08-24T10:00:00Z\"\n}\n```\n\n---\n\n## 7. Registro de escalamiento\n\n**Cuándo se genera:** Cuando el agente escala el caso a un humano.\n\n**Estructura:**\n\n```json\n{\n  \"escalamiento_id\": \"ONB-ESC-[timestamp]\",\n  \"agente\": \"onboarding-journey-agent\",\n  \"gimnasio_id\": \"[gimnasio_id]\",\n  \"gimnasio_nombre\": \"[gimnasio_nombre]\",\n  \"contacto_id\": \"[contacto_id]\",\n  \"nombre_miembro\": \"[nombre_miembro]\",\n  \"onboarding_id\": \"[onboarding_id]\",\n  \"motivo_escalamiento\": \"[motivo_enum]\",\n  \"descripcion_caso\": \"[texto_descriptivo]\",\n  \"urgencia\": \"[alta | media | baja]\",\n  \"asignado_a\": \"[nombre_humano_asignado]\",\n  \"timestamp_escalamiento\": \"[tiempo_fecha_actual]\",\n  \"datos_contexto\": {\n    \"onboarding_estado\": \"[onboarding_estado]\",\n    \"pasos_completados\": \"[pasos_completados]\",\n    \"pasos_pendientes\": \"[pasos_pendientes]\",\n    \"paso_bloqueado\": \"[paso_id]\",\n    \"recordatorios_enviados\": \"[total_recordatorios]\"\n  }\n}\n```\n\n**Ejemplo de registro de escalamiento:**\n\n```json\n{\n  \"escalamiento_id\": \"ONB-ESC-20260826100000\",\n  \"agente\": \"onboarding-journey-agent\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"contacto_id\": \"CT-2026-00701\",\n  \"nombre_miembro\": \"Lucía Martínez\",\n  \"onboarding_id\": \"ONB-ID-20260823-100000-001\",\n  \"motivo_escalamiento\": \"tercer_recordatorio_sin_respuesta\",\n  \"descripcion_caso\": \"El miembro Lucía Martínez recibió 3 recordatorios para completar el paso 'Reservar primera clase' y no completó ningún paso adicional después del perfil y método de pago. El onboarding lleva 3 días en progreso con 2 pasos completados de 5.\",\n  \"urgencia\": \"media\",\n  \"asignado_a\": \"Carlos Pérez (Entrenador principal)\",\n  \"timestamp_escalamiento\": \"2026-08-26T10:00:00Z\",\n  \"datos_contexto\": {\n    \"onboarding_estado\": \"en_progreso\",\n    \"pasos_completados\": 2,\n    \"pasos_pendientes\": 3,\n    \"paso_bloqueado\": \"reservar_primera_clase\",\n    \"recordatorios_enviados\": 6\n  }\n}\n```\n\n---\n\n## 8. Formato de fechas\n\n| Campo | Formato | Ejemplo |\n|---|---|---|\n| Fecha de alta | `DD/MM/YYYY HH:MM` | `\"23/08/2026 10:00\"` |\n| Fecha de cumplimiento | `DD/MM/YYYY HH:MM` | `\"23/08/2026 14:30\"` |\n| Fecha ISO 8601 | `YYYY-MM-DDTHH:MM:SS` | `\"2026-08-23T14:30:00Z\"` |\n| Hora | `HH:MM` | `\"14:30\"` |\n| Duración legible | `X días, Y horas` | `\"4 días, 23 horas\"` |\n\n---\n*Plantilla generada para el nicho Gimnasios y Fitness — Agente Onboarding Journey Agent*\n"
              },
              {
                "id": "onboarding-journey-agent/prompt-sistema.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "prompt-sistema.md",
                "contenido": "# Prompt del Sistema — Onboarding Journey Agent\n\n## Identidad\nEres **Onboarding Journey Agent**, un asistente automatizado de **[Nombre del Gimnasio]** especializado en guiar a los nuevos miembros a través del proceso de incorporación. Tu función es ejecutar el flujo de 5 pasos exactos que se define a continuación.\n\n## Tono de comunicación\n- Cálido y de bienvenida — es el primer contacto del miembro con el gimnasio\n- Claro y accionable — cada mensaje indica exactamente qué hacer\n- Motivador pero no invasivo — máximo 3 recordatorios por paso\n- Orientado a resultados — el objetivo es que complete el onboarding rápido\n\n## Flujo de ejecución\n\nEl agente ejecuta estos 5 pasos en orden, sin bifurcaciones ni condicionales:\n\n### Paso 1: Detecta el alta de un nuevo cliente/miembro en el sistema\nEl agente detecta cuando un nuevo miembro fue dado de alta y activa el flujo de onboarding con la checklist inicializada.\n\n### Paso 2: Envía la secuencia fija de mensajes de bienvenida con los pasos a seguir\nEl agente envía el mensaje de bienvenida con los 5 pasos del onboarding y sus enlaces directos.\n\n### Paso 3: Verifica el cumplimiento de cada paso contra una checklist\nEl agente verifica periódicamente el estado de cada paso: completar perfil, activar método de pago, reservar primera clase, asistir a evaluación física, primera clase completada.\n\n### Paso 4: Envía recordatorios automáticos para los pasos pendientes\nEl agente envía hasta 3 recordatorios por paso pendiente, con intervalos definidos. Al tercer recordatorio, escala a humano.\n\n### Paso 5: Marca el onboarding como completo cuando todos los pasos estén cumplidos\nEl agente envía felicitación al miembro y notifica al responsable humano con el resumen completo.\n\n## Ejemplo de ejecución\n\n**Entrada:**\n```\ncontacto_id: \"CT-2026-00701\"\nnombre_miembro: \"Lucía Martínez\"\nmembresia_tipo: \"premium\"\ncanal_preferido: \"whatsapp\"\n```\n\n**Ejecución paso a paso:**\n\n**Paso 1:** Detecta alta de Lucía Martínez. Inicializa checklist con 5 pasos pendiente.\n\n**Paso 2:** Envía mensaje de bienvenida con pasos:\n```\n¡Hola Lucía Martínez! ¡Bienvenido a FitZone Centro! 🎉💪\nTu membresía está activa. Estos son los pasos para empezar a entrenar:\n1️⃣ Completá tu perfil: https://app.fitzone.com/perfil/completar\n2️⃣ Activá tu método de pago: https://app.fitzone.com/pago/activar\n3️⃣ Reservá tu primera clase: https://app.fitzone.com/clases/reservar\n4️⃣ Asistí a tu evaluación física: https://app.fitzone.com/evaluacion/agendar\n```\n\n**Paso 3:** Verifica cumplimiento: perfil ✓, pago ✓, reserva pendiente...\n\n**Paso 4:** Envía recordatorio para reserva: \"¿Ya reservaste tu primera clase?\"\n\n**Paso 5:** Cuando todos los pasos completados → Felicita al miembro y notifica al entrenador.\n\n## Pasos del onboarding (5 pasos fijos)\n\n| # | Paso | Plazo máximo |\n|---|------|-------------|\n| 1 | Completar perfil | 48 horas |\n| 2 | Activar método de pago | 48 horas |\n| 3 | Reservar primera clase | 72 horas |\n| 4 | Asistir a evaluación física | 7 días |\n| 5 | Primera clase completada | 14 días |\n\n## Intervalos de recordatorio\n\n| Paso | 1er recordatorio | 2do recordatorio | 3er (escalación) |\n|------|-----------------|-----------------|------------------|\n| Perfil | 24h | 48h | 72h |\n| Pago | 24h | 48h | 72h |\n| Reserva | 48h | 72h | 96h |\n| Evaluación | 4 días | 6 días | 8 días |\n| Primera clase | 10 días | 12 días | 15 días |\n\n## Restricciones\n1. Ejecutar solo los 5 pasos del flujo, sin agregar pasos adicionales\n2. No saltar pasos ni enviar mensajes fuera de orden\n3. No enviar más de 3 recordatorios por paso\n4. No enviar mensajes fuera de horario de atención (07:00–22:00)\n5. Incluir enlaces directos en cada mensaje\n6. Confirmar cada paso completado con mensaje positivo\n7. No vender servicios adicionales — solo guiar el onboarding\n\n## Reglas de escalamiento\nEl agente escala a un humano en las siguientes situaciones:\n\n| Condición | Tiempo de respuesta |\n|-----------|---------------------|\n| Tercer recordatorio sin respuesta en ningún paso | 4 horas |\n| Miembro reporta problema técnico | 1 hora |\n| Miembro solicita ayuda excepcional | 2 horas |\n| Miembro indica que no desea continuar | 24 horas |\n| Error en el envío de mensajes (3 intentos fallidos) | 1 hora |\n| Todos los plazos expiraron sin completar onboarding | 24 horas |\n"
              },
              {
                "id": "onboarding-journey-agent/reglas-escalamiento.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "reglas-escalamiento.md",
                "contenido": "# Reglas de escalamiento — Onboarding Journey Agent\n\n## Descripción\n\nLista las condiciones exactas bajo las cuales el agente debe transferir el caso a un humano, sin dejar casos ambiguos. Cada condición tiene un identificador único, la situación que la activa, la acción a tomar y el plazo de respuesta.\n\n---\n\n## Condiciones de escalamiento\n\n### Categoría 1: Pasos incompletos después de múltiples recordatorios\n\n| ID | Condición | Acción del agente | Urgencia | Plazo de respuesta |\n|----|-----------|-------------------|----------|-------------------|\n| C1.1 | Un paso recibe el tercer recordatorio y sigue pendiente | Escalar a [entrenador responsable] con el historial de recordatorios y el paso bloqueado. El agente deja de enviar recordatorios para ese paso. | Media | 24 horas |\n| C1.2 | Dos o más pasos tienen el tercer recordatorio pendiente | Escalar a [gerencia] con el resumen completo del onboarding. Notificar al miembro que un humano lo contactará. | Alta | 4 horas |\n| C1.3 | Todos los pasos tienen al menos 1 recordatorio y ninguno fue completado | Escalar a [gerencia] con urgencia alta. El miembro puede estar teniendo problemas técnicos o no está interesado. | Alta | 4 horas |\n\n### Categoría 2: Problemas técnicos\n\n| ID | Condición | Acción del agente | Urgencia | Plazo de respuesta |\n|----|-----------|-------------------|----------|-------------------|\n| C2.1 | El envío del mensaje de bienvenida falla dos veces consecutivas | Registrar el error. Escalar a [equipo técnico] para verificar el canal de comunicación. El onboarding queda pausado hasta resolver. | Alta | 2 horas |\n| C2.2 | Los enlaces de onboarding (perfil, pago, clases, evaluación) devuelven error 404 o no cargan | Escalar a [equipo técnico] con los enlaces que fallan. No enviar más mensajes con enlaces rotos hasta resolver. | Alta | 4 horas |\n| C2.3 | El canal de comunicación preferido del miembro no está configurado o es inválido | Escalar a [equipo técnico] para verificar la configuración. Usar `email` como canal de respaldo si el email está disponible. | Media | 24 horas |\n| C2.4 | El sistema de verificación de pasos no responde o reporta errores | Escalar a [equipo técnico]. Los recordatorios se siguen enviando pero la verificación automática queda pausada. | Media | 24 horas |\n\n### Categoría 3: Datos del miembro incompletos o inválidos\n\n| ID | Condición | Acción del agente | Urgencia | Plazo de respuesta |\n|----|-----------|-------------------|----------|-------------------|\n| C3.1 | El `telefono_miembro` no tiene formato válido (menos de 8 o más de 15 dígitos) | No iniciar onboarding. Escalar a [equipo de datos] para verificar el registro del miembro. | Media | 24 horas |\n| C3.2 | El `email_miembro` no tiene formato válido | Escalar a [equipo de datos] para verificar. Si el teléfono es válido, continuar con WhatsApp como canal. | Media | 24 horas |\n| C3.3 | El miembro no existe en la base de datos del CRM después del alta | No iniciar onboarding. Escalar a [equipo de datos] para verificar la integridad del sistema de altas. | Alta | 4 horas |\n| C3.4 | La membresía no está en estado `activa` al momento del alta | No iniciar onboarding. Registrar como `membresia_no_activa` y escalar a [equipo de ventas]. | Media | 24 horas |\n\n### Categoría 4: Solicitudes especiales del miembro\n\n| ID | Condición | Acción del agente | Urgencia | Plazo de respuesta |\n|----|-----------|-------------------|----------|-------------------|\n| C4.1 | El miembro solicita ayuda excepcional para completar un paso (ej. no puede acceder a la app, no sabe usar la tecnología) | Escalar a [entrenador responsable] para asistencia personalizada. El agente pausa los recordatorios del paso afectado hasta recibir instrucción. | Media | 24 horas |\n| C4.2 | El miembro indica que no desea continuar con la membresía | Registrar como `cancelado`. Escalar a [equipo de ventas] para gestionar la baja o retención. No enviar más mensajes de onboarding. | Alta | 4 horas |\n| C4.3 | El miembro solicita pausar el onboarding por motivos personales (viaje, lesión, etc.) | Registrar como `pausado`. Pausar todos los recordatorios. Escalar a [entrenador responsable] para reprogramar. | Baja | 48 horas |\n| C4.4 | El miembro es patrocinador, influencer o tiene acuerdo comercial | Escalar a [gerencia] para verificar el acuerdo y dar instrucciones de manejo especial del onboarding. | Media | 4 horas |\n\n### Categoría 5: Conflictos de datos o verificación\n\n| ID | Condición | Acción del agente | Urgencia | Plazo de respuesta |\n|----|-----------|-------------------|----------|-------------------|\n| C5.1 | El sistema indica que un paso está completado pero el miembro reporta que no lo hizo | Escalar a [equipo técnico] para verificar la integridad de datos. No marcar el paso como completado hasta resolver. | Media | 24 horas |\n| C5.2 | El miembro completó un paso que no existe en la checklist configurada | Registrar la acción pero no afectar el progreso del onboarding. Escalar a [equipo de configuración] para verificar la checklist. | Baja | 48 horas |\n| C5.3 | El onboarding lleva más de 30 días sin completarse | Escalar a [gerencia] para evaluar si el caso debe cerrarse o reintentarse con un enfoque diferente. | Baja | 48 horas |\n\n### Categoría 6: Errores del sistema\n\n| ID | Condición | Acción del agente | Urgencia | Plazo de respuesta |\n|----|-----------|-------------------|----------|-------------------|\n| C6.1 | El `evento_id` recibido tiene formato inválido | No procesar. Escalar a [equipo técnico] para verificar el sistema de eventos. | Baja | 48 horas |\n| C6.2 | El mismo miembro recibe dos altas en un período de 24 horas | Verificar si es error del sistema o reactivación legítima. Si es error, escalar a [equipo técnico]. Si es reactivación, reiniciar el onboarding. | Media | 4 horas |\n| C6.3 | La fecha de alta es futura (mayor a `tiempo_fecha_actual`) | No procesar. Registrar como `fecha_invalida` y escalar a [equipo técnico]. | Baja | 48 horas |\n\n---\n\n## Reglas de asignación de urgencia\n\n| Nivel | Color | Significado | Tiempo máximo de espera |\n|-------|-------|-------------|------------------------|\n| **Alta** | 🔴 | Requiere intervención humana inmediata para evitar pérdida del miembro o error crítico | 4 horas |\n| **Media** | 🟡 | Requiere revisión humana para resolver la situación correctamente | 24 horas |\n| **Baja** | 🟢 | Informativo, requiere seguimiento pero no bloquea el onboarding | 48 horas |\n\n---\n\n## Reglas de notificación por urgencia\n\n| Urgencia | Canal de notificación | Destinatario |\n|----------|----------------------|--------------|\n| **Alta** | WhatsApp + Email | [Responsable directo] + [Supervisor] |\n| **Media** | Email | [Responsable directo] |\n| **Baja** | Registro en sistema | [Equipo de datos] |\n\n---\n\n## Textos base para escalamiento\n\n### Al miembro (cuando se escala):\n\n```\n¡Hola [nombre_miembro]! 👋\n\nParece que necesitás una mano para completar tu incorporación. Un miembro de nuestro equipo te va a contactar para ayudarte personalmente.\n\nMientras tanto, si tenés alguna duda, escribinos por este canal.\n— [gimnasio_nombre]\n```\n\n**Ejemplo:**\n\n```\n¡Hola Lucía Martínez! 👋\n\nParece que necesitás una mano para completar tu incorporación. Un miembro de nuestro equipo te va a contactar para ayudarte personalmente.\n\nMientras tanto, si tenés alguna duda, escribinos por este canal.\n— FitZone Centro\n```\n\n### Al responsable humano (notificación de escalamiento):\n\n```\n🚨 ESCALAMIENTO — Onboarding Journey Agent\n\n📋 Caso: [escalamiento_id]\n👤 Miembro: [nombre_miembro] ([contacto_id])\n📋 Membresía: [membresia_tipo]\n📅 Alta: [fecha_alta]\n⏱️ Días en progreso: [dias_en_progreso]\n\n⚠️ Motivo: [motivo_escalamiento]\n📝 Detalle: [descripcion_caso]\n🔴 Urgencia: [urgencia]\n⏰ Plazo de respuesta: [plazo] horas\n👤 Asignado a: [asignado_a]\n\n📊 Progreso del onboarding:\n- Pasos completados: [pasos_completados]/5\n- Paso bloqueado: [paso_bloqueado]\n- Recordatorios enviados: [recordatorios_enviados]\n```\n\n**Ejemplo:**\n\n```\n🚨 ESCALAMIENTO — Onboarding Journey Agent\n\n📋 Caso: ONB-ESC-20260826100000\n👤 Miembro: Lucía Martínez (CT-2026-00701)\n📋 Membresía: Premium\n📅 Alta: 23/08/2026 10:00\n⏱️ Días en progreso: 3\n\n⚠️ Motivo: tercer_recordatorio_sin_respuesta\n📝 Detalle: El miembro Lucía Martínez recibió 3 recordatorios para completar el paso 'Reservar primera clase' y no completó ningún paso adicional después del perfil y método de pago. El onboarding lleva 3 días en progreso con 2 pasos completados de 5.\n🔴 Urgencia: media\n⏰ Plazo de respuesta: 24 horas\n👤 Asignado a: Carlos Pérez (Entrenador principal)\n\n📊 Progreso del onboarding:\n- Pasos completados: 2/5\n- Paso bloqueado: reservar_primera_clase\n- Recordatorios enviados: 6\n```\n\n### Al equipo interno (notificación de escalación masiva):\n\n```\n🚨 ESCALAMIENTO MÚLTIPLE — Onboarding Journey Agent\n\n📋 Casos afectados: [total_casos]\n👤 Miembros: [lista_nombres]\n📅 Período: [fecha_inicio] – [fecha_fin]\n\n⚠️ Motivo: [motivo_general]\n📝 Detalle: [descripcion_general]\n🔴 Urgencia: alta\n⏰ Plazo de respuesta: 4 horas\n\nAcción requerida: Revisar cada caso individualmente y contactar a los miembros.\n```\n\n---\n\n## Ejemplo completo de escalamiento\n\n**Caso:** Lucía Martínez recibió 3 recordatorios para reservar su primera clase y no completó el paso.\n\n**Registro de escalamiento generado:**\n\n```json\n{\n  \"escalamiento_id\": \"ONB-ESC-20260826100000\",\n  \"agente\": \"onboarding-journey-agent\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"contacto_id\": \"CT-2026-00701\",\n  \"nombre_miembro\": \"Lucía Martínez\",\n  \"onboarding_id\": \"ONB-ID-20260823-100000-001\",\n  \"motivo_escalamiento\": \"tercer_recordatorio_sin_respuesta\",\n  \"descripcion_caso\": \"El miembro Lucía Martínez recibió 3 recordatorios para completar el paso 'Reservar primera clase' y no completó ningún paso adicional después del perfil y método de pago. El onboarding lleva 3 días en progreso con 2 pasos completados de 5.\",\n  \"urgencia\": \"media\",\n  \"asignado_a\": \"Carlos Pérez (Entrenador principal)\",\n  \"timestamp_escalamiento\": \"2026-08-26T10:00:00Z\",\n  \"datos_contexto\": {\n    \"onboarding_estado\": \"en_progreso\",\n    \"pasos_completados\": 2,\n    \"pasos_pendientes\": 3,\n    \"paso_bloqueado\": \"reservar_primera_clase\",\n    \"recordatorios_enviados\": 6\n  }\n}\n```\n\n**Acciones tomadas:**\n1. Se envió mensaje al miembro indicando que un humano lo contactará.\n2. Se notificó a Carlos Pérez por email.\n3. Se pausaron los recordatorios del paso \"Reservar primera clase\".\n4. Los recordatorios para \"Evaluación física\" y \"Primera clase completada\" siguen activos.\n5. El caso queda marcado como `escorado` hasta recibir instrucción del humano.\n\n---\n*Plantilla generada para el nicho Gimnasios y Fitness — Agente Onboarding Journey Agent*\n"
              },
              {
                "id": "onboarding-journey-agent/variables-entrada.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "variables-entrada.md",
                "contenido": "# Variables de entrada — Onboarding Journey Agent\n\nEste archivo define la lista fija y cerrada de campos de datos que el agente necesita recibir para operar. No se admiten campos adicionales a los listados aquí.\n\n---\n\n## Campos del evento disparador\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 1 | `evento_id` | `string` | Obligatorio | Identificador único del evento de alta. Formato: `ONB-YYYYMMDD-HHMMSS-XXX`. | `\"ONB-20260823-100000-001\"` |\n| 2 | `contacto_id` | `string` | Obligatorio | Identificador del contacto asociado al evento. Formato: `CT-YYYY-XXXXX`. | `\"CT-2026-00701\"` |\n| 3 | `evento_tipo` | `string` (enum) | Obligatorio | Tipo de evento. Valores: `alta_nueva`, `alta_transferencia`, `alta_reactivacion`. | `\"alta_nueva\"` |\n| 4 | `fecha_hora_alta` | `string` (ISO 8601) | Obligatorio | Fecha y hora en que se dio de alta al miembro. | `\"2026-08-23T10:00:00Z\"` |\n\n---\n\n## Datos del miembro\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 5 | `nombre_miembro` | `string` | Obligatorio | Nombre completo del miembro. | `\"Lucía Martínez\"` |\n| 6 | `telefono_miembro` | `string` | Obligatorio | Número telefónico del miembro. Entre 8 y 15 dígitos con código de país. | `\"+54 11 7777-1234\"` |\n| 7 | `email_miembro` | `string` | Obligatorio | Correo electrónico del miembro. | `\"lucia.martinez@email.com\"` |\n| 8 | `canal_preferido` | `string` (enum) | Obligatorio | Canal preferido para comunicación. Valores: `whatsapp`, `sms`, `email`. | `\"whatsapp\"` |\n| 9 | `membresia_tipo` | `string` (enum) | Obligatorio | Tipo de membresía contratada. Valores: `premium`, `basico`, `estudiante`, `corporativo`. | `\"premium\"` |\n| 10 | `membresia_estado` | `string` (enum) | Obligatorio | Estado de la membresía. Solo se procesan membresías con estado `activa`. | `\"activa\"` |\n| 11 | `membresia_fecha_inicio` | `string` (YYYY-MM-DD) | Obligatorio | Fecha de inicio de la membresía. | `\"2026-08-23\"` |\n| 12 | `membresia_plan_duracion_dias` | `integer` | Opcional | Duración del plan en días (30, 90, 180, 365). | `30` |\n\n---\n\n## Enlaces de onboarding (inyectados desde configuración)\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 13 | `enlace_perfil` | `string` | Obligatorio | URL directa para completar el perfil del miembro. | `\"https://app.fitzone.com/perfil/completar\"` |\n| 14 | `enlace_pago` | `string` | Obligatorio | URL directa para activar el método de pago. | `\"https://app.fitzone.com/pago/activar\"` |\n| 15 | `enlace_clases` | `string` | Obligatorio | URL directa para reservar la primera clase. | `\"https://app.fitzone.com/clases/reservar\"` |\n| 16 | `enlace_evaluacion` | `string` | Obligatorio | URL directa para agendar la evaluación física. | `\"https://app.fitzone.com/evaluacion/agendar\"` |\n\n---\n\n## Datos del gimnasio\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 17 | `gimnasio_id` | `string` | Obligatorio | Identificador único del gimnasio. | `\"GYM-001\"` |\n| 18 | `gimnasio_nombre` | `string` | Obligatorio | Nombre comercial del gimnasio. | `\"FitZone Centro\"` |\n| 19 | `gimnasio_telefono` | `string` | Opcional | Teléfono del gimnasio. | `\"+54 11 4444-5678\"` |\n| 20 | `gimnasio_direccion` | `string` | Opcional | Dirección física del gimnasio. | `\"Av. Principal 1234, CABA\"` |\n\n---\n\n## Datos de configuración de onboarding\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 21 | `pasos_onboarding` | `array` de objetos | Obligatorio | Lista de pasos del onboarding con sus plazos. | Ver estructura abajo |\n| 22 | `max_recordatorios_por_paso` | `integer` | Obligatorio (default: 3) | Máximo de recordatorios por paso antes de escalar. | `3` |\n| 23 | `intervalos_recordatorio` | `object` | Obligatorio | Mapa de paso → array de horas para recordatorios. | Ver estructura abajo |\n\n**Estructura de cada objeto en `pasos_onboarding`:**\n\n| Campo | Tipo | Descripción | Ejemplo |\n|---|---|---|---|\n| `paso_id` | `string` | Identificador del paso | `\"completar_perfil\"` |\n| `paso_nombre` | `string` | Nombre legible del paso | `\"Completar perfil\"` |\n| `plazo_horas` | `integer` | Horas desde el alta para completar el paso | `48` |\n| `campo_verificacion` | `string` | Campo del sistema a verificar | `\"perfil_estado\"` |\n| `condicion_cumplimiento` | `string` | Condición que debe cumplirse | `\"completado\"` |\n\n**Estructura de `intervalos_recordatorio`:**\n\n```json\n{\n  \"completar_perfil\": [24, 48, 72],\n  \"activar_metodo_pago\": [24, 48, 72],\n  \"reservar_primera_clase\": [48, 72, 96],\n  \"asistir_evaluacion_fisica\": [96, 144, 192],\n  \"primera_clase_completada\": [240, 288, 360]\n}\n```\n\n---\n\n## Datos de notificación\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 24 | `responsable_notificacion` | `string` | Obligatorio | Nombre de la persona que debe recibir la notificación de completado. | `\"Carlos Pérez (Entrenador principal)\"` |\n| 25 | `canal_notificacion_equipo` | `string` (enum) | Obligatorio | Canal para notificar al equipo. Valores: `email`, `whatsapp`, `app`. | `\"email\"` |\n| 26 | `email_responsable` | `string` | Condicional (requerido si `canal_notificacion_equipo` es `email`) | Correo electrónico del responsable. | `\"carlos.perez@fitzone.com\"` |\n| 27 | `telefono_responsable` | `string` | Condicional (requerido si `canal_notificacion_equipo` es `whatsapp`) | Teléfono del responsable. | `\"+54 11 4444-5678\"` |\n\n---\n\n## Datos de tiempos\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 28 | `tiempo_fecha_actual` | `string` (ISO 8601) | Obligatorio | Fecha y hora actual del sistema. | `\"2026-08-23T10:00:00Z\"` |\n| 29 | `horario_atencion_inicio` | `string` (HH:MM) | Opcional | Hora de inicio del horario de atención. Valor por defecto: `\"07:00\"`. | `\"07:00\"` |\n| 30 | `horario_atencion_fin` | `string` (HH:MM) | Opcional | Hora de fin del horario de atención. Valor por defecto: `\"22:00\"`. | `\"22:00\"` |\n\n---\n\n## Estado del onboarding (actualizado durante el flujo)\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 31 | `onboarding_estado` | `string` (enum) | Obligatorio (calculado) | Estado actual del onboarding. Valores: `en_progreso`, `completado`, `cancelado`, `escorado`, `vencido`. | `\"en_progreso\"` |\n| 32 | `checklist` | `array` de objetos | Obligatorio (calculado) | Estado de cada paso del onboarding. | Ver estructura abajo |\n\n**Estructura de cada objeto en `checklist`:**\n\n| Campo | Tipo | Descripción | Ejemplo |\n|---|---|---|---|\n| `paso_id` | `string` | Identificador del paso | `\"completar_perfil\"` |\n| `paso_nombre` | `string` | Nombre legible | `\"Completar perfil\"` |\n| `estado` | `string` (enum) | Estado del paso: `pendiente`, `completado`, `vencido` | `\"pendiente\"` |\n| `fecha_limite` | `string` (ISO 8601) | Fecha límite para completar | `\"2026-08-25T10:00:00Z\"` |\n| `fecha_cumplimiento` | `string` (ISO 8601) o `null` | Fecha en que se completó | `null` |\n| `recordatorios_enviados` | `integer` | Número de recordatorios enviados | `0` |\n| `fecha_ultimo_recordatorio` | `string` (ISO 8601) o `null` | Fecha del último recordatorio | `null` |\n\n---\n\n## Campos calculados por el agente\n\nEstos campos **no se reciben de entrada**; el agente los genera internamente.\n\n| # | Nombre del campo | Tipo de dato | Generado en | Descripción |\n|---|---|---|---|---|\n| 33 | `onboarding_id` | `string` | Paso 1 | ID único del onboarding. Formato: `ONB-ID-YYYYMMDD-HHMMSS-XXX`. |\n| 34 | `fecha_hora_deteccion_alta` | `string` (ISO 8601) | Paso 1 | Marca de tiempo de detección del alta. |\n| 35 | `fecha_hora_envio_bienvenida` | `string` (ISO 8601) o `null` | Paso 2 | Marca de tiempo de envío del mensaje de bienvenida. |\n| 36 | `pasos_completados` | `integer` | Paso 3 | Número de pasos completados hasta el momento. |\n| 37 | `pasos_pendientes` | `integer` | Paso 3 | Número de pasos pendientes. |\n| 38 | `porcentaje_completado` | `number` | Paso 3 | Porcentaje de avance (0–100). |\n| 39 | `fecha_hora_completado` | `string` (ISO 8601) o `null` | Paso 5 | Marca de tiempo de completado del onboarding. |\n| 40 | `duracion_onboarding_horas` | `number` | Paso 5 | Duración total del onboarding en horas. |\n| 41 | `duracion_onboarding_texto` | `string` | Paso 5 | Duración en formato legible (ej. \"5 días, 6 horas\"). |\n| 42 | `resultado` | `string` (enum) | Paso 5 | Resultado final: `completado`, `cancelado`, `escorado`, `vencido`. |\n| 43 | `fecha_hora_cierre` | `string` (ISO 8601) | Paso 5 | Marca de tiempo de cierre del caso. |\n\n---\n\n## Validaciones de entrada\n\n| Campo | Regla | Acción si falla |\n|---|---|---|\n| `evento_id` | No vacío, formato válido | Terminar flujo sin ejecutar |\n| `contacto_id` | Debe seguir formato `CT-YYYY-XXXXX` | Terminar flujo sin ejecutar |\n| `nombre_miembro` | No vacío, mínimo 2 caracteres | Terminar flujo sin ejecutar |\n| `telefono_miembro` | Entre 8 y 15 dígitos con código de país | Escalar a humano (C4.1) |\n| `email_miembro` | Formato de email válido | Escalar a humano (C4.1) |\n| `membresia_tipo` | Debe ser uno de los 4 valores válidos | Terminar flujo sin ejecutar |\n| `membresia_estado` | Debe ser `activa` | No iniciar onboarding |\n| `membresia_fecha_inicio` | Formato YYYY-MM-DD, no futura | Usar `fecha_hora_alta` como fecha de inicio |\n| `enlace_perfil` | No vacío, formato URL válido | Usar URL genérica del gimnasio |\n| `enlace_pago` | No vacío, formato URL válido | Usar URL genérica del gimnasio |\n| `enlace_clases` | No vacío, formato URL válido | Usar URL genérica del gimnasio |\n| `enlace_evaluacion` | No vacío, formato URL válido | Usar URL genérica del gimnasio |\n| `gimnasio_id` | No vacío | Terminar flujo sin ejecutar |\n| `responsable_notificacion` | No vacío | Terminar flujo sin ejecutar |\n| `canal_notificacion_equipo` | Debe ser uno de los 3 valores válidos | Usar `email` como valor por defecto |\n| `email_responsable` | Requerido si `canal_notificacion_equipo` es `email` | Escalar a humano (C4.1) |\n\n---\n\n## Ejemplo de payload completo\n\n```json\n{\n  \"evento_id\": \"ONB-20260823-100000-001\",\n  \"contacto_id\": \"CT-2026-00701\",\n  \"evento_tipo\": \"alta_nueva\",\n  \"fecha_hora_alta\": \"2026-08-23T10:00:00Z\",\n  \"nombre_miembro\": \"Lucía Martínez\",\n  \"telefono_miembro\": \"+54 11 7777-1234\",\n  \"email_miembro\": \"lucia.martinez@email.com\",\n  \"canal_preferido\": \"whatsapp\",\n  \"membresia_tipo\": \"premium\",\n  \"membresia_estado\": \"activa\",\n  \"membresia_fecha_inicio\": \"2026-08-23\",\n  \"membresia_plan_duracion_dias\": 30,\n  \"enlace_perfil\": \"https://app.fitzone.com/perfil/completar\",\n  \"enlace_pago\": \"https://app.fitzone.com/pago/activar\",\n  \"enlace_clases\": \"https://app.fitzone.com/clases/reservar\",\n  \"enlace_evaluacion\": \"https://app.fitzone.com/evaluacion/agendar\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"gimnasio_telefono\": \"+54 11 4444-5678\",\n  \"gimnasio_direccion\": \"Av. Principal 1234, CABA\",\n  \"pasos_onboarding\": [\n    {\"paso_id\": \"completar_perfil\", \"paso_nombre\": \"Completar perfil\", \"plazo_horas\": 48, \"campo_verificacion\": \"perfil_estado\", \"condicion_cumplimiento\": \"completado\"},\n    {\"paso_id\": \"activar_metodo_pago\", \"paso_nombre\": \"Activar método de pago\", \"plazo_horas\": 48, \"campo_verificacion\": \"metodo_pago_estado\", \"condicion_cumplimiento\": \"activo\"},\n    {\"paso_id\": \"reservar_primera_clase\", \"paso_nombre\": \"Reservar primera clase\", \"plazo_horas\": 72, \"campo_verificacion\": \"reservas_clases\", \"condicion_cumplimiento\": \"al_menos_1_reserva\"},\n    {\"paso_id\": \"asistir_evaluacion_fisica\", \"paso_nombre\": \"Asistir a evaluación física\", \"plazo_horas\": 168, \"campo_verificacion\": \"evaluacion_estado\", \"condicion_cumplimiento\": \"completada\"},\n    {\"paso_id\": \"primera_clase_completada\", \"paso_nombre\": \"Primera clase completada\", \"plazo_horas\": 336, \"campo_verificacion\": \"historial_clases\", \"condicion_cumplimiento\": \"al_menos_1_clase_completada\"}\n  ],\n  \"max_recordatorios_por_paso\": 3,\n  \"intervalos_recordatorio\": {\n    \"completar_perfil\": [24, 48, 72],\n    \"activar_metodo_pago\": [24, 48, 72],\n    \"reservar_primera_clase\": [48, 72, 96],\n    \"asistir_evaluacion_fisica\": [96, 144, 192],\n    \"primera_clase_completada\": [240, 288, 360]\n  },\n  \"responsable_notificacion\": \"Carlos Pérez (Entrenador principal)\",\n  \"canal_notificacion_equipo\": \"email\",\n  \"email_responsable\": \"carlos.perez@fitzone.com\",\n  \"telefono_responsable\": null,\n  \"tiempo_fecha_actual\": \"2026-08-23T10:00:00Z\",\n  \"horario_atencion_inicio\": \"07:00\",\n  \"horario_atencion_fin\": \"22:00\"\n}\n```\n\n---\n*Plantilla generada para el nicho Gimnasios y Fitness — Agente Onboarding Journey Agent*\n"
              }
            ]
          }
        ]
      },
      {
        "id": "failed-payment",
        "carpeta": "failed-payment-follow-up",
        "nombre": "Failed Payment Follow-up",
        "icono": "💰",
        "descripcion": "Gestionar pagos fallidos con recordatorios y opciones de reintento",
        "submodulos": [],
        "categoria": "general",
        "estructura": [
          {
            "id": "failed-payment-follow-up",
            "etiqueta": "",
            "es_plantilla": true,
            "nombre": "failed-payment-follow-up",
            "subcarpetas": [],
            "notas": [
              {
                "id": "failed-payment-follow-up/flujo.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "flujo.md",
                "contenido": "# Flujo de ejecución — Failed Payment Follow-up\n\nEste archivo documenta el flujo fijo de ejecución del agente. Cada caso de pago fallido sigue estos 6 pasos **en orden**, sin pasos condicionales ni bifurcaciones.\n\n---\n\n## Paso 1 — Detección del evento disparador (pago fallido)\n\nEl agente detecta que un pago de membresía no pudo ser procesado y activa el flujo de seguimiento.\n\n**Acción del agente:**\n1. Recibir los datos del pago fallido según lo definido en `variables-entrada.md`.\n2. Registrar la marca de tiempo de detección en `fecha_hora_deteccion_fallo`.\n3. Validar que el `contacto_id` tenga un formato válido.\n4. Verificar que el `evento_tipo` sea `pago_fallido`.\n5. Verificar que no exista un caso abierto previo para el mismo miembro y el mismo período de facturación.\n6. Determinar el canal de comunicación preferido del miembro.\n7. Construir el `enlace_pago` completo.\n8. Calcular la `fecha_limite_suspension`: `fecha_hora_deteccion_fallo` + `dias_limite_suspension` días.\n9. Generar el `caso_id` único.\n\n**Ejemplo de evento detectado:**\n\n```json\n{\n  \"evento_id\": \"PAY-FAIL-20260823-143000-001\",\n  \"contacto_id\": \"CT-2026-00452\",\n  \"evento_tipo\": \"pago_fallido\",\n  \"nombre_miembro\": \"María López\",\n  \"telefono_miembro\": \"+54 11 5555-1234\",\n  \"email_miembro\": \"maria.lopez@email.com\",\n  \"canal_preferido\": \"whatsapp\",\n  \"membresia_tipo\": \"premium\",\n  \"membresia_estado\": \"activa\",\n  \"membresia_fecha_vencimiento\": \"2026-08-23\",\n  \"pago_id\": \"PAY-20260823-001\",\n  \"monto_pago\": 15000,\n  \"moneda\": \"ARS\",\n  \"fecha_pago_original\": \"2026-08-23\",\n  \"motivo_fallo\": \"fondos_insuficientes\",\n  \"motivo_fallo_detalle\": \"Fondos insuficientes en la tarjeta terminada en 4532\",\n  \"metodo_pago_tipo\": \"tarjeta_credito\",\n  \"metodo_pago_ultimos_4\": \"4532\",\n  \"intentos_previos\": 0,\n  \"max_intentos_reintento\": 2,\n  \"dias_limite_suspension\": 7,\n  \"enlace_pago\": \"https://app.fitzone.com/pago/actualizar\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"gimnasio_telefono\": \"+54 11 4444-5678\",\n  \"responsable_cobranza\": \"Departamento de Cobranzas\",\n  \"email_responsable\": \"cobranzas@fitzone.com\",\n  \"horario_atencion_inicio\": \"08:00\",\n  \"horario_atencion_fin\": \"20:00\",\n  \"tiempo_fecha_actual\": \"2026-08-23T14:30:00Z\"\n}\n```\n\n---\n\n## Paso 2 — Espera del intervalo de tiempo definido\n\nEl agente espera el intervalo de tiempo configurado antes de enviar el primer mensaje de seguimiento.\n\n**Acción del agente:**\n1. Determinar el intervalo de espera según el tipo de evento:\n   - Pago fallido: 4 horas.\n   - Reintento fallido: 2 horas.\n2. Calcular el `momento_envio_seguimiento`: `fecha_hora_deteccion_fallo` + intervalo.\n3. Verificar si el `momento_envio_seguimiento` cae dentro del horario de atención (08:00–20:00):\n   - Si es antes de las 08:00 → ajustar a las 10:00 del mismo día.\n   - Si es después de las 20:00 → ajustar a las 10:00 del siguiente día hábil.\n4. Registrar el `momento_envio_seguimiento_ajustado`.\n5. Esperar hasta que se alcance el momento de envío.\n6. Registrar la marca de tiempo de inicio de espera en `fecha_hora_inicio_espera`.\n\n**Tabla de intervalos de espera:**\n\n| Tipo de evento | Intervalo | Ejemplo (fallo 23/ago 14:30) |\n|---|---|---|\n| Pago fallido | 4 horas | Envío: 23/ago 18:30 → dentro de horario |\n| Reintento fallido | 2 horas | Envío: 23/ago 16:30 → dentro de horario |\n\n**Ejemplo de cálculo (pago fallido):**\n\n```json\n{\n  \"tipo_evento\": \"pago_fallido\",\n  \"intervalo_horas\": 4,\n  \"fecha_hora_deteccion_fallo\": \"2026-08-23T14:30:00Z\",\n  \"momento_envio_seguimiento_calculado\": \"2026-08-23T18:30:00Z\",\n  \"momento_envio_seguimiento_ajustado\": \"2026-08-23T18:30:00Z\",\n  \"ajuste_horario\": false,\n  \"razon_ajuste\": null,\n  \"fecha_limite_suspension\": \"2026-08-30T14:30:00Z\"\n}\n```\n\n**Ejemplo de cálculo (pago fallido fuera de horario):**\n\n```json\n{\n  \"tipo_evento\": \"pago_fallido\",\n  \"intervalo_horas\": 4,\n  \"fecha_hora_deteccion_fallo\": \"2026-08-23T18:00:00Z\",\n  \"momento_envio_seguimiento_calculado\": \"2026-08-23T22:00:00Z\",\n  \"momento_envio_seguimiento_ajustado\": \"2026-08-24T10:00:00Z\",\n  \"ajuste_horario\": true,\n  \"razon_ajuste\": \"Momento calculado fuera de horario (22:00), ajustado a 10:00 del siguiente día hábil\",\n  \"fecha_limite_suspension\": \"2026-08-30T18:00:00Z\"\n}\n```\n\n---\n\n## Paso 3 — Redacción del mensaje de seguimiento personalizado\n\nEl agente genera el mensaje de seguimiento usando la plantilla de `formato-salida.md` → sección 1, insertando los datos específicos del caso.\n\n**Acción del agente:**\n1. Seleccionar la plantilla de mensaje según el número de seguimiento (1º, 2º o 3º).\n2. Insertar los datos del caso: nombre, monto, moneda, motivo de fallo, fecha límite, enlace de pago.\n3. Formatear el monto con separadores de miles.\n4. Formatear las fechas en formato `DD/MM/YYYY`.\n5. Generar el mensaje en texto plano (WhatsApp/SMS) y en HTML (email).\n\n**Plantilla de primer seguimiento (WhatsApp/SMS):**\n\n```\nHola [nombre_miembro], tu pago de [monto_formateado] [moneda] por la membresía [membresia_tipo] no pudo ser procesado el [fecha_pago_original].\n\nMotivo: [motivo_fallo_legible]\n\nPara evitar la suspensión de tu acceso antes del [fecha_limite_formateada], actualizá tu método de pago:\n👉 [enlace_pago]\n\nSi ya lo hiciste, ignorá este mensaje. Si tenés dudas, escribinos.\n— [gimnasio_nombre]\n```\n\n**Plantilla de segundo seguimiento (WhatsApp/SMS):**\n\n```\nHola [nombre_miembro], te recordamos que tu pago de [monto_formateado] [moneda] sigue pendiente.\n\nTu acceso se suspenderá el [fecha_limite_formateada] si no se regulariza la situación.\n\nActualizá tu método de pago acá:\n👉 [enlace_pago]\n\n¿Necesitás ayuda? Escribinos por este canal.\n— [gimnasio_nombre]\n```\n\n**Plantilla de tercer seguimiento (WhatsApp/SMS) — Último aviso:**\n\n```\nHola [nombre_miembro], este es tu último aviso antes de la suspensión de tu membresía.\n\nPago pendiente: [monto_formateado] [moneda]\nFecha límite: [fecha_limite_formateada]\n\nActualizá tu método de pago ahora:\n👉 [enlace_pago]\n\nSi ya pagaste, ignorá este mensaje. Si tenés un problema, escribinos y te ayudamos.\n— [gimnasio_nombre]\n```\n\n**Plantilla de seguimiento (email):**\n\n**Asunto:** Pago pendiente — [gimnasio_nombre]\n\n```\nHola [nombre_miembro],\n\nTu pago de [monto_formateado] [moneda] por la membresía [membresia_tipo] no pudo ser procesado el [fecha_pago_original].\n\nMotivo del fallo: [motivo_fallo_legible]\nDetalle: [motivo_fallo_detalle]\n\nPara evitar la suspensión de tu acceso antes del [fecha_limite_formateada], actualizá tu método de pago:\n\n👉 [enlace_pago]\n\nSi ya realizaste el pago, por favor ignorá este mensaje.\n\n¿Tenés dudas? Respondé a este email o escribinos por WhatsApp al [gimnasio_telefono].\n\nSaludos,\n— El equipo de [gimnasio_nombre]\n```\n\n**Ejemplo renderizado de primer seguimiento (WhatsApp):**\n\n```\nHola María López, tu pago de $15.000 ARS por la membresía premium no pudo ser procesado el 23/08/2026.\n\nMotivo: Fondos insuficientes en la tarjeta\n\nPara evitar la suspensión de tu acceso antes del 30/08/2026, actualizá tu método de pago:\n👉 https://app.fitzone.com/pago/actualizar\n\nSi ya lo hiciste, ignorá este mensaje. Si tenés dudas, escribinos.\n— FitZone Centro\n```\n\n**Ejemplo renderizado de segundo seguimiento (WhatsApp):**\n\n```\nHola María López, te recordamos que tu pago de $15.000 ARS sigue pendiente.\n\nTu acceso se suspenderá el 30/08/2026 si no se regulariza la situación.\n\nActualizá tu método de pago acá:\n👉 https://app.fitzone.com/pago/actualizar\n\n¿Necesitás ayuda? Escribinos por este canal.\n— FitZone Centro\n```\n\n**Ejemplo renderizado de tercer seguimiento (WhatsApp):**\n\n```\nHola María López, este es tu último aviso antes de la suspensión de tu membresía.\n\nPago pendiente: $15.000 ARS\nFecha límite: 30/08/2026\n\nActualizá tu método de pago ahora:\n👉 https://app.fitzone.com/pago/actualizar\n\nSi ya pagaste, ignorá este mensaje. Si tenés un problema, escribinos y te ayudamos.\n— FitZone Centro\n```\n\n---\n\n## Paso 4 — Envío del mensaje por el canal configurado\n\nEl agente envía el mensaje de seguimiento al miembro por el canal preferido.\n\n**Acción del agente:**\n1. Seleccionar el canal de envío (`canal_preferido`).\n2. Enviar el mensaje generado en el paso 3.\n3. Si el envío falla, reintentar una sola vez después de 30 segundos.\n4. Si el segundo intento falla, registrar el error y activar escalamiento (ver `reglas-escalamiento.md` → C3.1).\n5. Registrar la marca de tiempo de envío en `fecha_hora_envio_seguimiento`.\n6. Incrementar el contador `seguimientos_enviados`.\n7. Actualizar el estado del caso a `seguimiento_enviado`.\n8. Iniciar el contador de plazo de respuesta:\n   - Primer seguimiento: 48 horas.\n   - Segundo seguimiento: 24 horas.\n   - Tercer seguimiento: 48 horas (antes de escalar).\n9. Calcular el `momento_proximo_seguimiento` si corresponde.\n\n**Tabla de plazos de respuesta:**\n\n| Número de seguimiento | Plazo de respuesta | Acción si no responde |\n|---|---|---|\n| 1º seguimiento | 48 horas | Enviar 2º seguimiento |\n| 2º seguimiento | 24 horas | Enviar 3º seguimiento |\n| 3º seguimiento | 48 horas | Escalar a humano |\n\n---\n\n## Paso 5 — Registro de la respuesta del contacto en el historial\n\nEl agente espera la respuesta del miembro dentro del plazo definido y la registra en el historial.\n\n**Acción del agente:**\n1. Esperar la respuesta del miembro dentro del plazo establecido.\n2. Si el miembro responde, registrar:\n   - Texto de la respuesta.\n   - Fecha y hora de la respuesta.\n   - Clasificación de la respuesta (ver tabla abajo).\n3. Si el plazo expira sin respuesta, registrar como `sin_respuesta`.\n4. Actualizar el historial del contacto con la interacción completa.\n5. Proceder al paso 6.\n\n**Tabla de clasificación de respuestas:**\n\n| Respuesta del miembro | Clasificación | Acción |\n|---|---|---|\n| \"Ya pagué\", \"Listo\", \"Lo solucioné\", \"Ya actualicé la tarjeta\" | `pago_confirmado` | Verificar en sistema → si pagado, cerrar caso |\n| \"No tengo dinero ahora\", \"La semana que viene\", \"Espero unos días\" | `solicitud_plazo` | Escalar a humano (C2.1) |\n| \"La app no funciona\", \"No puedo entrar a la página\", \"No me llega el código\" | `problema_tecnico` | Escalar a humano (C3.1) |\n| \"Quiero cancelar la membresía\", \"No voy a seguir\" | `solicitud_cancelacion` | Escalar a humano (C4.1) |\n| \"Ya pagué pero me siguen molestando\", \"Esto es acoso\" | `queja` | Escalar a humano (C5.1) |\n| Sin respuesta en el plazo | `sin_respuesta` | Enviar siguiente seguimiento o escalar |\n\n**Ejemplo de registro de respuesta:**\n\n```json\n{\n  \"respuesta_id\": \"RESP-20260824-100000-001\",\n  \"contacto_id\": \"CT-2026-00452\",\n  \"nombre_miembro\": \"María López\",\n  \"caso_id\": \"CASE-20260823-143000-001\",\n  \"texto_respuesta\": \"Ya actualicé mi tarjeta, debería funcionar ahora\",\n  \"fecha_hora_respuesta\": \"2026-08-24T10:00:00Z\",\n  \"clasificacion\": \"pago_confirmado\",\n  \"seguimiento_numero\": 1,\n  \"verificacion_pago\": {\n    \"pago_verificado\": true,\n    \"pago_estado\": \"procesado\",\n    \"pago_fecha_procesado\": \"2026-08-24T10:05:00Z\",\n    \"monto_procesado\": 15000\n  }\n}\n```\n\n---\n\n## Paso 6 — Marcado del caso como cerrado o escalamiento a humano\n\nEl agente procesa la respuesta del miembro y toma la acción correspondiente.\n\n**Acción del agente:**\n\n**Si la respuesta es `pago_confirmado`:**\n1. Verificar en el sistema si el pago fue procesado.\n2. Si fue procesado:\n   - Marcar el caso como `cerrado_pago_recuperado`.\n   - Enviar mensaje de confirmación al miembro.\n   - Actualizar el estado de la membresía a `activa`.\n3. Si no fue procesado:\n   - Enviar mensaje indicando que el pago aún no se refleja.\n   - Mantener el caso abierto.\n\n**Si la respuesta es `solicitud_plazo`, `problema_tecnico`, `solicitud_cancelacion` o `queja`:**\n1. Generar el registro de escalamiento (ver `formato-salida.md` → sección 6).\n2. Enviar mensaje al miembro indicando que un humano lo contactará.\n3. Marcar el caso como `escorado`.\n4. Notificar al responsable correspondiente.\n\n**Si la respuesta es `sin_respuesta` y se agotaron los seguimientos:**\n1. Generar el registro de escalamiento.\n2. Marcar el caso como `escorado_sin_respuesta`.\n3. Notificar al responsable de cobranzas.\n\n**Tabla de estados del caso:**\n\n| Estado | Condición |\n|---|---|\n| `abierto` | Pago fallido detectado, esperando primer seguimiento |\n| `seguimiento_enviado` | Al menos 1 mensaje de seguimiento enviado |\n| `respuesta_recibida` | El miembro respondió, procesando respuesta |\n| `cerrado_pago_recuperado` | El pago fue procesado después del seguimiento |\n| `cerrado_sin_pago` | Se agotaron los seguimientos, caso escalado |\n| `escorado` | El caso fue transferido a humano |\n| `cancelado` | El miembro solicitó cancelar la membresía |\n\n**Plantilla de confirmación de pago (WhatsApp/SMS):**\n\n```\n¡[nombre_miembro], tu pago fue procesado! ✅\n\nMonto: [monto_formateado] [moneda]\nMembresía: [membresia_tipo]\nEstado: Activa\n\nTu acceso sigue vigente. ¡Nos vemos en el gym! 💪\n— [gimnasio_nombre]\n```\n\n**Ejemplo renderizado de confirmación (WhatsApp):**\n\n```\n¡María López, tu pago fue procesado! ✅\n\nMonto: $15.000 ARS\nMembresía: Premium\nEstado: Activa\n\nTu acceso sigue vigente. ¡Nos vemos en el gym! 💪\n— FitZone Centro\n```\n\n**Plantilla de pago no reflejado (WhatsApp/SMS):**\n\n```\nHola [nombre_miembro], indicaste que ya pagaste pero aún no vemos el movimiento en nuestro sistema.\n\nSi ya actualizaste tu método de pago, puede tardar hasta 24 horas en reflejarse. Si el problema persiste, escribinos y te ayudamos.\n\n— [gimnasio_nombre]\n```\n\n**Plantilla de escalamiento al miembro (WhatsApp/SMS):**\n\n```\nHola [nombre_miembro], tu caso requiere atención personalizada de nuestro equipo.\n\nUn miembro de [gimnasio_nombre] te contactará en las próximas horas para ayudarte.\n\nSi tenés alguna duda, escribinos por este canal.\n— [gimnasio_nombre]\n```\n\n---\n*Plantilla generada para el nicho Gimnasios y Fitness — Agente Failed Payment Follow-up*\n"
              },
              {
                "id": "failed-payment-follow-up/formato-salida.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "formato-salida.md",
                "contenido": "# Formato de salida — Failed Payment Follow-up\n\n## Descripción\n\nDefine la estructura exacta del mensaje o registro que el agente debe producir en cada paso del flujo. Todos los campos deben tener valores concretos de ejemplo.\n\n---\n\n## 1. Mensaje de seguimiento — Primer aviso\n\n**Cuándo se genera:** Paso 3 — 4 horas después del pago fallido.\n\n**Plantilla (WhatsApp/SMS):**\n\n```\nHola [nombre_miembro], tu pago de [monto_formateado] [moneda] por la membresía [membresia_tipo] no pudo ser procesado el [fecha_pago_original].\n\nMotivo: [motivo_fallo_legible]\n\nPara evitar la suspensión de tu acceso antes del [fecha_limite_formateada], actualizá tu método de pago:\n👉 [enlace_pago]\n\nSi ya lo hiciste, ignorá este mensaje. Si tenés dudas, escribinos.\n— [gimnasio_nombre]\n```\n\n**Tabla de motivos legibles:**\n\n| Código | Texto legible |\n|---|---|\n| `fondos_insuficientes` | Fondos insuficientes en la tarjeta |\n| `tarjeta_vencida` | La tarjeta de pago expiró |\n| `tarjeta_bloqueada` | La tarjeta fue bloqueada por el banco |\n| `datos_incorrectos` | Los datos de pago son incorrectos |\n| `error_sistema` | Error técnico al procesar el pago |\n| `limite_superado` | La tarjeta alcanzó su límite de crédito |\n| `desconocido` | No se pudo determinar el motivo |\n\n**Ejemplo renderizado (WhatsApp):**\n\n```\nHola María López, tu pago de $15.000 ARS por la membresía premium no pudo ser procesado el 23/08/2026.\n\nMotivo: Fondos insuficientes en la tarjeta\n\nPara evitar la suspensión de tu acceso antes del 30/08/2026, actualizá tu método de pago:\n👉 https://app.fitzone.com/pago/actualizar\n\nSi ya lo hiciste, ignorá este mensaje. Si tenés dudas, escribinos.\n— FitZone Centro\n```\n\n---\n\n## 2. Mensaje de seguimiento — Segundo aviso\n\n**Cuándo se genera:** 24 horas después del primer aviso (si no hay respuesta).\n\n**Plantilla (WhatsApp/SMS):**\n\n```\nHola [nombre_miembro], te recordamos que tu pago de [monto_formateado] [moneda] sigue pendiente.\n\nTu acceso se suspenderá el [fecha_limite_formateada] si no se regulariza la situación.\n\nActualizá tu método de pago acá:\n👉 [enlace_pago]\n\n¿Necesitás ayuda? Escribinos por este canal.\n— [gimnasio_nombre]\n```\n\n**Ejemplo renderizado (WhatsApp):**\n\n```\nHola María López, te recordamos que tu pago de $15.000 ARS sigue pendiente.\n\nTu acceso se suspenderá el 30/08/2026 si no se regulariza la situación.\n\nActualizá tu método de pago acá:\n👉 https://app.fitzone.com/pago/actualizar\n\n¿Necesitás ayuda? Escribinos por este canal.\n— FitZone Centro\n```\n\n---\n\n## 3. Mensaje de seguimiento — Tercer aviso (último)\n\n**Cuándo se genera:** 48 horas después del segundo aviso (si no hay respuesta).\n\n**Plantilla (WhatsApp/SMS):**\n\n```\nHola [nombre_miembro], este es tu último aviso antes de la suspensión de tu membresía.\n\nPago pendiente: [monto_formateado] [moneda]\nFecha límite: [fecha_limite_formateada]\n\nActualizá tu método de pago ahora:\n👉 [enlace_pago]\n\nSi ya pagaste, ignorá este mensaje. Si tenés un problema, escribinos y te ayudamos.\n— [gimnasio_nombre]\n```\n\n**Ejemplo renderizado (WhatsApp):**\n\n```\nHola María López, este es tu último aviso antes de la suspensión de tu membresía.\n\nPago pendiente: $15.000 ARS\nFecha límite: 30/08/2026\n\nActualizá tu método de pago ahora:\n👉 https://app.fitzone.com/pago/actualizar\n\nSi ya pagaste, ignorá este mensaje. Si tenés un problema, escribinos y te ayudamos.\n— FitZone Centro\n```\n\n---\n\n## 4. Mensaje de confirmación de pago procesado\n\n**Cuándo se genera:** Paso 6 — Cuando el pago fue procesado después del seguimiento.\n\n**Plantilla (WhatsApp/SMS):**\n\n```\n¡[nombre_miembro], tu pago fue procesado! ✅\n\nMonto: [monto_formateado] [moneda]\nMembresía: [membresia_tipo]\nEstado: Activa\n\nTu acceso sigue vigente. ¡Nos vemos en el gym! 💪\n— [gimnasio_nombre]\n```\n\n**Ejemplo renderizado (WhatsApp):**\n\n```\n¡María López, tu pago fue procesado! ✅\n\nMonto: $15.000 ARS\nMembresía: Premium\nEstado: Activa\n\nTu acceso sigue vigente. ¡Nos vemos en el gym! 💪\n— FitZone Centro\n```\n\n---\n\n## 5. Mensaje de pago no reflejado\n\n**Cuándo se genera:** Paso 6 — Cuando el miembro dice que pagó pero el sistema no lo registra.\n\n**Plantilla (WhatsApp/SMS):**\n\n```\nHola [nombre_miembro], indicaste que ya pagaste pero aún no vemos el movimiento en nuestro sistema.\n\nSi ya actualizaste tu método de pago, puede tardar hasta 24 horas en reflejarse. Si el problema persiste, escribinos y te ayudamos.\n\n— [gimnasio_nombre]\n```\n\n**Ejemplo renderizado (WhatsApp):**\n\n```\nHola María López, indicaste que ya pagaste pero aún no vemos el movimiento en nuestro sistema.\n\nSi ya actualizaste tu método de pago, puede tardar hasta 24 horas en reflejarse. Si el problema persiste, escribinos y te ayudamos.\n\n— FitZone Centro\n```\n\n---\n\n## 6. Mensaje de escalamiento al miembro\n\n**Cuándo se genera:** Paso 6 — Cuando el caso se transfiere a humano.\n\n**Plantilla (WhatsApp/SMS):**\n\n```\nHola [nombre_miembro], tu caso requiere atención personalizada de nuestro equipo.\n\nUn miembro de [gimnasio_nombre] te contactará en las próximas horas para ayudarte.\n\nSi tenés alguna duda, escribinos por este canal.\n— [gimnasio_nombre]\n```\n\n**Ejemplo renderizado (WhatsApp):**\n\n```\nHola María López, tu caso requiere atención personalizada de nuestro equipo.\n\nUn miembro de FitZone Centro te contactará en las próximas horas para ayudarte.\n\nSi tenés alguna duda, escribinos por este canal.\n— FitZone Centro\n```\n\n---\n\n## 7. Registro de acción (JSON)\n\n**Cuándo se genera:** En cada paso del flujo, como registro interno.\n\n**Estructura:**\n\n```json\n{\n  \"registro_id\": \"FPF-REG-[timestamp]\",\n  \"agente\": \"failed-payment-follow-up\",\n  \"caso_id\": \"[caso_id]\",\n  \"contacto_id\": \"[contacto_id]\",\n  \"nombre_miembro\": \"[nombre_miembro]\",\n  \"gimnasio_id\": \"[gimnasio_id]\",\n  \"gimnasio_nombre\": \"[gimnasio_nombre]\",\n  \"pago_id\": \"[pago_id]\",\n  \"monto_pago\": \"[monto_pago]\",\n  \"moneda\": \"[moneda]\",\n  \"motivo_fallo\": \"[motivo_fallo]\",\n  \"membresia_tipo\": \"[membresia_tipo]\",\n  \"fecha_limite_suspension\": \"[fecha_limite_suspension]\",\n  \"seguimientos_enviados\": \"[seguimientos_enviados]\",\n  \"seguimiento_numero\": \"[seguimiento_numero]\",\n  \"fecha_hora_envio_seguimiento\": \"[fecha_hora_envio_seguimiento]\",\n  \"respuesta_miembro\": \"[respuesta_miembro]\",\n  \"texto_respuesta\": \"[texto_respuesta]\",\n  \"fecha_hora_respuesta\": \"[fecha_hora_respuesta]\",\n  \"verificacion_pago\": \"[verificacion_pago]\",\n  \"estado_caso\": \"[estado_caso]\",\n  \"resultado\": \"[resultado]\",\n  \"fecha_hora_cierre\": \"[fecha_hora_cierre]\",\n  \"duracion_caso_horas\": \"[duracion_caso_horas]\",\n  \"timestamp_registro\": \"[tiempo_fecha_actual]\"\n}\n```\n\n**Ejemplo de registro completo (pago recuperado):**\n\n```json\n{\n  \"registro_id\": \"FPF-REG-20260824100000\",\n  \"agente\": \"failed-payment-follow-up\",\n  \"caso_id\": \"CASE-20260823-143000-001\",\n  \"contacto_id\": \"CT-2026-00452\",\n  \"nombre_miembro\": \"María López\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"pago_id\": \"PAY-20260823-001\",\n  \"monto_pago\": 15000,\n  \"moneda\": \"ARS\",\n  \"motivo_fallo\": \"fondos_insuficientes\",\n  \"membresia_tipo\": \"premium\",\n  \"fecha_limite_suspension\": \"2026-08-30T14:30:00Z\",\n  \"seguimientos_enviados\": 1,\n  \"seguimiento_numero\": 1,\n  \"fecha_hora_envio_seguimiento\": \"2026-08-23T18:30:00Z\",\n  \"respuesta_miembro\": \"pago_confirmado\",\n  \"texto_respuesta\": \"Ya actualicé mi tarjeta, debería funcionar ahora\",\n  \"fecha_hora_respuesta\": \"2026-08-24T10:00:00Z\",\n  \"verificacion_pago\": {\n    \"pago_verificado\": true,\n    \"pago_estado\": \"procesado\",\n    \"pago_fecha_procesado\": \"2026-08-24T10:05:00Z\",\n    \"monto_procesado\": 15000\n  },\n  \"estado_caso\": \"cerrado_pago_recuperado\",\n  \"resultado\": \"pago_recuperado\",\n  \"fecha_hora_cierre\": \"2026-08-24T10:10:00Z\",\n  \"duracion_caso_horas\": 19.67,\n  \"timestamp_registro\": \"2026-08-24T10:10:00Z\"\n}\n```\n\n**Ejemplo de registro completo (caso escalado — solicitud de plazo):**\n\n```json\n{\n  \"registro_id\": \"FPF-REG-20260825100000\",\n  \"agente\": \"failed-payment-follow-up\",\n  \"caso_id\": \"CASE-20260823-143000-002\",\n  \"contacto_id\": \"CT-2026-00389\",\n  \"nombre_miembro\": \"Roberto García\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"pago_id\": \"PAY-20260823-002\",\n  \"monto_pago\": 12000,\n  \"moneda\": \"ARS\",\n  \"motivo_fallo\": \"tarjeta_bloqueada\",\n  \"membresia_tipo\": \"basico\",\n  \"fecha_limite_suspension\": \"2026-08-30T14:30:00Z\",\n  \"seguimientos_enviados\": 2,\n  \"seguimiento_numero\": 2,\n  \"fecha_hora_envio_seguimiento\": \"2026-08-25T10:00:00Z\",\n  \"respuesta_miembro\": \"solicitud_plazo\",\n  \"texto_respuesta\": \"No tengo la plata ahora, la semana que viene\",\n  \"fecha_hora_respuesta\": \"2026-08-25T10:00:00Z\",\n  \"verificacion_pago\": null,\n  \"estado_caso\": \"escorado\",\n  \"resultado\": \"escalado_solicitud_plazo\",\n  \"fecha_hora_cierre\": \"2026-08-25T10:05:00Z\",\n  \"duracion_caso_horas\": 43.58,\n  \"timestamp_registro\": \"2026-08-25T10:05:00Z\"\n}\n```\n\n**Ejemplo de registro completo (sin respuesta — caso escalado):**\n\n```json\n{\n  \"registro_id\": \"FPF-REG-20260828100000\",\n  \"agente\": \"failed-payment-follow-up\",\n  \"caso_id\": \"CASE-20260823-143000-003\",\n  \"contacto_id\": \"CT-2026-00501\",\n  \"nombre_miembro\": \"Lucas Fernández\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"pago_id\": \"PAY-20260823-003\",\n  \"monto_pago\": 15000,\n  \"moneda\": \"ARS\",\n  \"motivo_fallo\": \"datos_incorrectos\",\n  \"membresia_tipo\": \"premium\",\n  \"fecha_limite_suspension\": \"2026-08-30T14:30:00Z\",\n  \"seguimientos_enviados\": 3,\n  \"seguimiento_numero\": 3,\n  \"fecha_hora_envio_seguimiento\": \"2026-08-28T10:00:00Z\",\n  \"respuesta_miembro\": \"sin_respuesta\",\n  \"texto_respuesta\": null,\n  \"fecha_hora_respuesta\": null,\n  \"verificacion_pago\": null,\n  \"estado_caso\": \"escorado_sin_respuesta\",\n  \"resultado\": \"escalado_sin_respuesta\",\n  \"fecha_hora_cierre\": \"2026-08-28T10:05:00Z\",\n  \"duracion_caso_horas\": 117.58,\n  \"timestamp_registro\": \"2026-08-28T10:05:00Z\"\n}\n```\n\n---\n\n## 8. Registro de escalamiento\n\n**Cuándo se genera:** Cuando el agente escala el caso a un humano.\n\n**Estructura:**\n\n```json\n{\n  \"escalamiento_id\": \"FPF-ESC-[timestamp]\",\n  \"agente\": \"failed-payment-follow-up\",\n  \"gimnasio_id\": \"[gimnasio_id]\",\n  \"gimnasio_nombre\": \"[gimnasio_nombre]\",\n  \"contacto_id\": \"[contacto_id]\",\n  \"nombre_miembro\": \"[nombre_miembro]\",\n  \"caso_id\": \"[caso_id]\",\n  \"pago_id\": \"[pago_id]\",\n  \"monto_pago\": \"[monto_pago]\",\n  \"moneda\": \"[moneda]\",\n  \"motivo_escalamiento\": \"[motivo_enum]\",\n  \"descripcion_caso\": \"[texto_descriptivo]\",\n  \"urgencia\": \"[alta | media | baja]\",\n  \"asignado_a\": \"[nombre_humano_asignado]\",\n  \"timestamp_escalamiento\": \"[tiempo_fecha_actual]\",\n  \"datos_contexto\": {\n    \"motivo_fallo\": \"[motivo_fallo]\",\n    \"membresia_tipo\": \"[membresia_tipo]\",\n    \"seguimientos_enviados\": \"[seguimientos_enviados]\",\n    \"respuesta_miembro\": \"[respuesta_miembro]\",\n    \"fecha_limite_suspension\": \"[fecha_limite_suspension]\"\n  }\n}\n```\n\n**Ejemplo de registro de escalamiento:**\n\n```json\n{\n  \"escalamiento_id\": \"FPF-ESC-20260825100000\",\n  \"agente\": \"failed-payment-follow-up\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"contacto_id\": \"CT-2026-00389\",\n  \"nombre_miembro\": \"Roberto García\",\n  \"caso_id\": \"CASE-20260823-143000-002\",\n  \"pago_id\": \"PAY-20260823-002\",\n  \"monto_pago\": 12000,\n  \"moneda\": \"ARS\",\n  \"motivo_escalamiento\": \"solicitud_plazo\",\n  \"descripcion_caso\": \"El miembro Roberto García indicó que no puede pagar ahora y solicita una semana de plazo. Su tarjeta está bloqueada y no tiene alternativa de pago inmediata. Membresía básica vence el 30/08/2026.\",\n  \"urgencia\": \"media\",\n  \"asignado_a\": \"Departamento de Cobranzas\",\n  \"timestamp_escalamiento\": \"2026-08-25T10:05:00Z\",\n  \"datos_contexto\": {\n    \"motivo_fallo\": \"tarjeta_bloqueada\",\n    \"membresia_tipo\": \"basico\",\n    \"seguimientos_enviados\": 2,\n    \"respuesta_miembro\": \"solicitud_plazo\",\n    \"fecha_limite_suspension\": \"2026-08-30T14:30:00Z\"\n  }\n}\n```\n\n---\n\n## 9. Formato de fechas\n\n| Campo | Formato | Ejemplo |\n|---|---|---|\n| Fecha de pago | `DD/MM/YYYY` | `\"23/08/2026\"` |\n| Fecha límite | `DD/MM/YYYY` | `\"30/08/2026\"` |\n| Monto | `$[monto] [moneda]` | `\"$15.000 ARS\"` |\n| Fecha ISO 8601 | `YYYY-MM-DDTHH:MM:SS` | `\"2026-08-23T14:30:00Z\"` |\n| Hora | `HH:MM` | `\"14:30\"` |\n\n---\n*Plantilla generada para el nicho Gimnasios y Fitness — Agente Failed Payment Follow-up*\n"
              },
              {
                "id": "failed-payment-follow-up/prompt-sistema.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "prompt-sistema.md",
                "contenido": "# Prompt del Sistema — Failed Payment Follow-up\n\n## Identidad\nEres **Failed Payment Follow-up Agent**, un asistente automatizado de **[Nombre del Gimnasio]** especializado en dar seguimiento automático a pagos fallidos de membresías. Tu función es ejecutar el flujo de 6 pasos exactos que se define a continuación.\n\n## Tono de comunicación\n- Comprensivo y no juzgante — los pagos fallidos pueden ocurrir por muchos motivos\n- Directo y claro — indica exactamente qué ocurrió y qué hacer para resolverlo\n- Orientado a la solución — el objetivo es que regularice su pago lo antes posible\n- Sin presionar en exceso — máximo 3 mensajes de seguimiento\n\n## Flujo de ejecución\n\nEl agente ejecuta estos 6 pasos en orden, sin bifurcaciones ni condicionales:\n\n### Paso 1: Detecta que ocurrió el evento disparador (pago fallido)\nEl agente detecta que un pago de membresía no pudo ser procesado y activa el flujo de seguimiento.\n\n### Paso 2: Espera el intervalo de tiempo definido en la plantilla\nEl agente espera 4 horas después del fallo (o 2 horas si es reintento fallido) antes de continuar.\n\n### Paso 3: Redacta un mensaje de seguimiento personalizado con los datos del caso\nEl agente genera el mensaje con: nombre del miembro, monto, motivo de fallo, fecha límite de suspensión y enlace directo para actualizar método de pago.\n\n### Paso 4: Envía el mensaje por el canal configurado\nEl agente envía el mensaje por WhatsApp, SMS o email según la preferencia del miembro.\n\n### Paso 5: Registra la respuesta del contacto en el historial\nEl agente espera la respuesta dentro del plazo definido (48h primer mensaje, 24h segundo, 48h tercero) y la registra en el historial.\n\n### Paso 6: Marca el caso como cerrado o lo escala a un humano según la respuesta recibida\nEl agente procesa la respuesta y toma la acción correspondiente: confirma pago y cierra, o escala a humano.\n\n## Ejemplo de ejecución\n\n**Entrada:**\n```\nevento_tipo: \"pago_fallido\"\ncontacto_id: \"CT-2026-00452\"\nnombre_miembro: \"María López\"\nmonto_pago: 15000\nmoneda: \"ARS\"\nmotivo_fallo: \"fondos_insuficientes\"\ncanal_preferido: \"whatsapp\"\n```\n\n**Ejecución paso a paso:**\n\n**Paso 1:** Detecta pago fallido de María López por $15.000 ARS (fondos insuficientes). Fecha límite de suspensión: 30/08/2026.\n\n**Paso 2:** Espera 4 horas (14:30 → 18:30).\n\n**Paso 3:** Redacta mensaje:\n```\nHola María López, tu pago de $15.000 ARS por la membresía premium no pudo ser procesado el 23/08/2026.\nMotivo: Fondos insuficientes en la tarjeta\nPara evitar la suspensión de tu acceso antes del 30/08/2026, actualizá tu método de pago:\n👉 https://app.fitzone.com/pago/actualizar\nSi ya lo hiciste, ignorá este mensaje. Si tenés dudas, escribinos.\n— FitZone Centro\n```\n\n**Paso 4:** Envía por WhatsApp.\n\n**Paso 5:** María responde: \"Ya actualicé mi tarjeta\". Se registra como `pago_confirmado`.\n\n**Paso 6:** Se verifica el pago en el sistema → procesado. Se cierra el caso como `cerrado_pago_recuperado`.\n\n## Motivos de pago fallido (7 tipos)\n\n| Código | Motivo |\n|--------|--------|\n| `fondos_insuficientes` | La tarjeta no tiene fondos suficientes |\n| `tarjeta_vencida` | La tarjeta expiró |\n| `tarjeta_bloqueada` | La tarjeta fue bloqueada por el banco |\n| `datos_incorrectos` | Número, vencimiento o CVV incorrecto |\n| `error_sistema` | Error técnico del procesador de pagos |\n| `limite_superado` | La tarjeta alcanzó su límite |\n| `desconocido` | Motivo no identificado |\n\n## Intervalos de seguimiento\n\n| Evento | 1er mensaje | 2do mensaje | 3er mensaje |\n|--------|------------|------------|------------|\n| Pago fallido | 4 horas después | 24h después del 1er | 48h después del 2do |\n| Reintento fallido | 2 horas después | 12h después del 1er | 24h después del 2do |\n\n## Restricciones\n1. Ejecutar solo los 6 pasos del flujo, sin agregar pasos adicionales\n2. No emitir juicios ni culpar al miembro por el pago fallido\n3. Siempre incluir enlace directo para actualizar método de pago\n4. Indicar la fecha límite antes de la suspensión de acceso\n5. Máximo 3 mensajes por caso de pago fallido\n6. No compartir información de otros miembros ni del procesador de pagos\n\n## Reglas de escalamiento\nEl agente escala a un humano en las siguientes situaciones:\n\n| Condición | Tiempo de respuesta |\n|-----------|---------------------|\n| Miembro reporta problema técnico con la app o página | 1 hora |\n| Miembro solicita plazo de gracia para pagar | 24 horas |\n| Miembro indica que desea cancelar la membresía | 4 horas |\n| Miembro genera una queja o se siente acosado | 2 horas |\n| Tercer seguimiento enviado sin respuesta | 4 horas |\n| Error en el envío de mensajes (3 intentos fallidos) | 1 hora |\n| La membresía se vence por falta de pago | 24 horas |\n"
              },
              {
                "id": "failed-payment-follow-up/reglas-escalamiento.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "reglas-escalamiento.md",
                "contenido": "# Reglas de escalamiento — Failed Payment Follow-up\n\n## Descripción\n\nLista las condiciones exactas bajo las cuales el agente debe transferir el caso a un humano, sin dejar casos ambiguos. Cada condición tiene un identificador único, la situación que la activa, la acción a tomar y el plazo de respuesta.\n\n---\n\n## Condiciones de escalamiento\n\n### Categoría 1: Pagos fallidos recurrentes\n\n| ID | Condición | Acción del agente | Urgencia | Plazo de respuesta |\n|----|-----------|-------------------|----------|-------------------|\n| C1.1 | El mismo miembro tiene 3 o más pagos fallidos consecutivos en los últimos 90 días | Escalar a [gerencia/encargado de cobranzas] con el historial completo de pagos fallidos. | Alta | 4 horas |\n| C1.2 | El `intentos_previos` supera el `max_intentos_reintento` | No realizar más reintentos automáticos. Escalar a [departamento de cobranzas] para gestión manual. | Media | 24 horas |\n| C1.3 | El monto del pago fallido supera el monto promedio de las membresías del gimnasio (detectar anomalía) | Escalar a [departamento de cobranzas] para verificar si es error o caso especial. | Media | 24 horas |\n\n### Categoría 2: Solicitudes del miembro que requieren intervención humana\n\n| ID | Condición | Acción del agente | Urgencia | Plazo de respuesta |\n|----|-----------|-------------------|----------|-------------------|\n| C2.1 | El miembro solicita un plazo o fraccionamiento del pago | Escalar a [departamento de cobranzas] con los detalles de la solicitud. No ofrecer plazos sin autorización. | Media | 24 horas |\n| C2.2 | El miembro indica que tiene un problema financiero grave (desempleo, emergencia médica) | Escalar a [gerencia] con sensibilidad y discreción. Ofrecer opciones de pausa si están configuradas. | Media | 24 horas |\n| C2.3 | El miembro solicita cancelar la membresía por el pago fallido | Escalar a [equipo de retención] con los detalles del caso. No procesar la cancelación automáticamente. | Alta | 4 horas |\n| C2.4 | El miembro indica que no es su cuenta o que alguien más hizo el registro fraudulento | Escalar a [seguridad/gerencia] inmediatamente. Congelar el caso hasta verificación. | Alta | 2 horas |\n\n### Categoría 3: Problemas técnicos\n\n| ID | Condición | Acción del agente | Urgencia | Plazo de respuesta |\n|----|-----------|-------------------|----------|-------------------|\n| C3.1 | El miembro reporta que la app o página de pago no funciona | Escalar a [equipo técnico] con los detalles del error. No enviar más enlaces de pago hasta resolver. | Media | 24 horas |\n| C3.2 | El envío del mensaje de seguimiento falla dos veces consecutivas | Registrar el error. Escalar a [equipo técnico] para verificar el canal de comunicación. El caso queda pendiente. | Media | 24 horas |\n| C3.3 | El canal de comunicación preferido del miembro no está configurado o es inválido | Escalar a [equipo técnico] para verificar la configuración. Usar `email` como canal de respaldo si está disponible. | Baja | 48 horas |\n| C3.4 | El `evento_id` o `pago_id` tiene formato inválido | No procesar. Escalar a [equipo técnico] para verificar el sistema de eventos. | Baja | 48 horas |\n\n### Categoría 4: Datos del miembro incompletos o inválidos\n\n| ID | Condición | Acción del agente | Urgencia | Plazo de respuesta |\n|----|-----------|-------------------|----------|-------------------|\n| C4.1 | El `telefono_miembro` no tiene formato válido (menos de 8 o más de 15 dígitos) | No enviar mensajes SMS/WhatsApp. Escalar a [equipo de datos] para verificar el registro. | Media | 24 horas |\n| C4.2 | El `email_miembro` no tiene formato válido y el canal es `email` | Escalar a [equipo de datos] para verificar. Si el teléfono es válido, usar WhatsApp como respaldo. | Baja | 48 horas |\n| C4.3 | El miembro no existe en la base de datos del CRM después del evento de pago fallido | No iniciar seguimiento. Escalar a [equipo de datos] para verificar la integridad del sistema. | Alta | 4 horas |\n| C4.4 | La membresía ya está en estado `suspendida` al momento del evento de pago fallido | No enviar mensajes de seguimiento de pago. Escalar a [departamento de cobranzas] para gestión de reactivación. | Media | 24 horas |\n\n### Categoría 5: Quejas o problemas con el servicio\n\n| ID | Condición | Acción del agente | Urgencia | Plazo de respuesta |\n|----|-----------|-------------------|----------|-------------------|\n| C5.1 | El miembro expresa una queja sobre el servicio del gimnasio como razón del no pago | Escalar a [gerencia/servicio al cliente] con los detalles de la queja. No continuar con el seguimiento de cobranza hasta resolver la queja. | Alta | 4 horas |\n| C5.2 | El miembro indica que fue cobrado indebidamente o que el monto es incorrecto | Escalar a [departamento de cobranzas] para verificar el monto. Congelar el seguimiento hasta verificación. | Alta | 4 horas |\n| C5.3 | El miembro solicita hablar con un humano específico (gerente, encargado) | Escalar a la persona solicitada si es identificable. Si no, escalar a [gerencia]. | Media | 4 horas |\n\n### Categoría 6: Errores del sistema o datos inconsistentes\n\n| ID | Condición | Acción del agente | Urgencia | Plazo de respuesta |\n|----|-----------|-------------------|----------|-------------------|\n| C6.1 | El pago fue procesado exitosamente pero el evento de fallo no se actualizó | Verificar y actualizar el estado del caso a `cerrado_pago_recuperado`. No escalar a menos que haya más de 1 caso afectado. | Baja | 48 horas |\n| C6.2 | Hay más de 5 casos de pago fallido en las últimas 24 horas para el mismo gimnasio | Escalar a [gerencia + equipo técnico] para evaluar si hay un problema sistémico con el procesador de pagos. | Alta | 2 horas |\n| C6.3 | El `monto_pago` es 0 o negativo | No procesar. Escalar a [equipo técnico] para verificar la integridad de datos. | Media | 24 horas |\n| C6.4 | La `fecha_limite_suspension` ya pasó al momento de detectar el fallo | Escalar a [departamento de cobranzas] para gestión inmediata de suspensión o reactivación. | Alta | 2 horas |\n\n---\n\n## Reglas de asignación de urgencia\n\n| Nivel | Color | Significado | Tiempo máximo de espera |\n|-------|-------|-------------|------------------------|\n| **Alta** | 🔴 | Requiere intervención humana inmediata para evitar pérdida de ingreso, fraude o error sistémico | 4 horas |\n| **Media** | 🟡 | Requiere revisión humana para resolver la situación correctamente | 24 horas |\n| **Baja** | 🟢 | Informativo, requiere seguimiento pero no bloquea el proceso de cobranza | 48 horas |\n\n---\n\n## Reglas de notificación por urgencia\n\n| Urgencia | Canal de notificación | Destinatario |\n|----------|----------------------|--------------|\n| **Alta** | WhatsApp + Email | [Responsable directo] + [Supervisor] |\n| **Media** | Email | [Responsable directo] |\n| **Baja** | Registro en sistema | [Equipo de datos] |\n\n---\n\n## Textos base para escalamiento\n\n### Al miembro (cuando se escala):\n\n```\nHola [nombre_miembro], tu caso requiere atención personalizada de nuestro equipo.\n\nUn miembro de [gimnasio_nombre] te contactará en las próximas horas para ayudarte.\n\nSi tenés alguna duda, escribinos por este canal.\n— [gimnasio_nombre]\n```\n\n**Ejemplo:**\n\n```\nHola Roberto García, tu caso requiere atención personalizada de nuestro equipo.\n\nUn miembro de FitZone Centro te contactará en las próximas horas para ayudarte.\n\nSi tenés alguna duda, escribinos por este canal.\n— FitZone Centro\n```\n\n### Al equipo interno (notificación de escalamiento):\n\n```\n🚨 ESCALAMIENTO — Failed Payment Follow-up\n\n📋 Caso: [escalamiento_id]\n👤 Miembro: [nombre_miembro] ([contacto_id])\n💳 Pago: [monto_formateado] [moneda] ([pago_id])\n📋 Membresía: [membresia_tipo]\n📅 Fecha límite: [fecha_limite_formateada]\n\n⚠️ Motivo: [motivo_escalamiento]\n📝 Detalle: [descripcion_caso]\n🔴 Urgencia: [urgencia]\n⏰ Plazo de respuesta: [plazo] horas\n👤 Asignado a: [asignado_a]\n\n📊 Contexto:\n- Motivo del fallo: [motivo_fallo]\n- Seguimientos enviados: [seguimientos_enviados]\n- Respuesta del miembro: [respuesta_miembro]\n```\n\n**Ejemplo:**\n\n```\n🚨 ESCALAMIENTO — Failed Payment Follow-up\n\n📋 Caso: FPF-ESC-20260825100000\n👤 Miembro: Roberto García (CT-2026-00389)\n💳 Pago: $12.000 ARS (PAY-20260823-002)\n📋 Membresía: Básico\n📅 Fecha límite: 30/08/2026\n\n⚠️ Motivo: solicitud_plazo\n📝 Detalle: El miembro Roberto García indicó que no puede pagar ahora y solicita una semana de plazo. Su tarjeta está bloqueada y no tiene alternativa de pago inmediata.\n🔴 Urgencia: media\n⏰ Plazo de respuesta: 24 horas\n👤 Asignado a: Departamento de Cobranzas\n\n📊 Contexto:\n- Motivo del fallo: tarjeta_bloqueada\n- Seguimientos enviados: 2\n- Respuesta del miembro: solicitud_plazo\n```\n\n### Al equipo interno (notificación de casos sistémicos):\n\n```\n🚨 ALERTA SISTÉMICA — Múltiples pagos fallidos\n\n📋 Casos afectados: [total_casos]\n📅 Período: [fecha_inicio] – [fecha_fin]\n💰 Monto total afectado: [monto_total_formateado]\n\n⚠️ Posible problema con el procesador de pagos o configuración de cobros.\n🔴 Urgencia: alta\n⏰ Plazo de respuesta: 2 horas\n\nAcción requerida: Verificar el estado del procesador de pagos y contactar a los miembros afectados.\n```\n\n---\n\n## Ejemplo completo de escalamiento\n\n**Caso:** Roberto García solicita una semana de plazo para pagar su membresía básica.\n\n**Registro de escalamiento generado:**\n\n```json\n{\n  \"escalamiento_id\": \"FPF-ESC-20260825100000\",\n  \"agente\": \"failed-payment-follow-up\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"contacto_id\": \"CT-2026-00389\",\n  \"nombre_miembro\": \"Roberto García\",\n  \"caso_id\": \"CASE-20260823-143000-002\",\n  \"pago_id\": \"PAY-20260823-002\",\n  \"monto_pago\": 12000,\n  \"moneda\": \"ARS\",\n  \"motivo_escalamiento\": \"solicitud_plazo\",\n  \"descripcion_caso\": \"El miembro Roberto García indicó que no puede pagar ahora y solicita una semana de plazo. Su tarjeta está bloqueada y no tiene alternativa de pago inmediata. Membresía básica vence el 30/08/2026.\",\n  \"urgencia\": \"media\",\n  \"asignado_a\": \"Departamento de Cobranzas\",\n  \"timestamp_escalamiento\": \"2026-08-25T10:05:00Z\",\n  \"datos_contexto\": {\n    \"motivo_fallo\": \"tarjeta_bloqueada\",\n    \"membresia_tipo\": \"basico\",\n    \"seguimientos_enviados\": 2,\n    \"respuesta_miembro\": \"solicitud_plazo\",\n    \"fecha_limite_suspension\": \"2026-08-30T14:30:00Z\"\n  }\n}\n```\n\n**Acciones tomadas:**\n1. Se envió mensaje al miembro indicando que un humano lo contactará.\n2. Se notificó al Departamento de Cobranzas por email.\n3. Se pausó el envío de seguimientos automáticos.\n4. El caso quedó marcado como `escorado` hasta recibir instrucción del humano.\n\n---\n*Plantilla generada para el nicho Gimnasios y Fitness — Agente Failed Payment Follow-up*\n"
              },
              {
                "id": "failed-payment-follow-up/variables-entrada.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "variables-entrada.md",
                "contenido": "# Variables de entrada — Failed Payment Follow-up\n\nEste archivo define la lista fija y cerrada de campos de datos que el agente necesita recibir para operar. No se admiten campos adicionales a los listados aquí.\n\n---\n\n## Campos del evento disparador\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 1 | `evento_id` | `string` | Obligatorio | Identificador único del evento de pago fallido. Formato: `PAY-FAIL-YYYYMMDD-HHMMSS-XXX`. | `\"PAY-FAIL-20260823-143000-001\"` |\n| 2 | `contacto_id` | `string` | Obligatorio | Identificador del contacto asociado al evento. Formato: `CT-YYYY-XXXXX`. | `\"CT-2026-00452\"` |\n| 3 | `evento_tipo` | `string` (enum) | Obligatorio | Tipo de evento. Valores: `pago_fallido`, `reintento_fallido`. | `\"pago_fallido\"` |\n| 4 | `fecha_hora_deteccion_fallo` | `string` (ISO 8601) | Obligatorio | Fecha y hora en que se detectó el pago fallido. | `\"2026-08-23T14:30:00Z\"` |\n\n---\n\n## Datos del miembro\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 5 | `nombre_miembro` | `string` | Obligatorio | Nombre completo del miembro. | `\"María López\"` |\n| 6 | `telefono_miembro` | `string` | Obligatorio (requerido para whatsapp/sms) | Número telefónico del miembro. Entre 8 y 15 dígitos con código de país. | `\"+54 11 5555-1234\"` |\n| 7 | `email_miembro` | `string` | Opcional (requerido si `canal_preferido` es `email`) | Correo electrónico del miembro. | `\"maria.lopez@email.com\"` |\n| 8 | `canal_preferido` | `string` (enum) | Obligatorio | Canal preferido para comunicación. Valores: `whatsapp`, `sms`, `email`. | `\"whatsapp\"` |\n\n---\n\n## Datos de la membresía\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 9 | `membresia_tipo` | `string` (enum) | Obligatorio | Tipo de membresía. Valores: `premium`, `basico`, `estudiante`, `corporativo`. | `\"premium\"` |\n| 10 | `membresia_estado` | `string` (enum) | Obligatorio | Estado de la membresía al momento del fallo. Valores: `activa`, `en_riesgo`, `suspendida`. | `\"activa\"` |\n| 11 | `membresia_fecha_vencimiento` | `string` (YYYY-MM-DD) | Obligatorio | Fecha de vencimiento de la membresía. | `\"2026-08-23\"` |\n| 12 | `membresia_plan_duracion_dias` | `integer` | Opcional | Duración del plan en días. | `30` |\n\n---\n\n## Datos del pago\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 13 | `pago_id` | `string` | Obligatorio | Identificador único del pago. Formato: `PAY-YYYYMMDD-XXX`. | `\"PAY-20260823-001\"` |\n| 14 | `monto_pago` | `number` | Obligatorio | Monto del pago en la moneda local. | `15000` |\n| 15 | `moneda` | `string` (ISO 4217) | Obligatorio | Código de moneda. | `\"ARS\"` |\n| 16 | `fecha_pago_original` | `string` (YYYY-MM-DD) | Obligatorio | Fecha en que se intentó procesar el pago. | `\"2026-08-23\"` |\n| 17 | `motivo_fallo` | `string` (enum) | Obligatorio | Código del motivo de fallo. Valores: `fondos_insuficientes`, `tarjeta_vencida`, `tarjeta_bloqueada`, `datos_incorrectos`, `error_sistema`, `limite_superado`, `desconocido`. | `\"fondos_insuficientes\"` |\n| 18 | `motivo_fallo_detalle` | `string` | Opcional | Detalle adicional del motivo de fallo proporcionado por el procesador. | `\"Fondos insuficientes en la tarjeta terminada en 4532\"` |\n| 19 | `metodo_pago_tipo` | `string` (enum) | Opcional | Tipo de método de pago. Valores: `tarjeta_credito`, `tarjeta_debito`, `transferencia`, `qr`, `efectivo`. | `\"tarjeta_credito\"` |\n| 20 | `metodo_pago_ultimos_4` | `string` | Opcional | Últimos 4 dígitos de la tarjeta (para referencia). | `\"4532\"` |\n\n---\n\n## Datos de reintentos\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 21 | `intentos_previos` | `integer` | Obligatorio (default: 0) | Número de intentos de pago fallidos previos para este mismo período. | `0` |\n| 22 | `max_intentos_reintento` | `integer` | Obligatorio (default: 2) | Máximo de reintentos automáticos antes de escalar. | `2` |\n| 23 | `reintento_automatico_habilitado` | `boolean` | Obligatorio (default: true) | Si el sistema debe reintentar el cobro automáticamente. | `true` |\n\n---\n\n## Datos de plazos\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 24 | `dias_limite_suspension` | `integer` | Obligatorio (default: 7) | Días desde el fallo antes de suspender el acceso. | `7` |\n| 25 | `intervalo_primer_seguimiento_horas` | `integer` | Obligatorio (default: 4) | Horas de espera para el primer mensaje de seguimiento. | `4` |\n| 26 | `intervalo_segundo_seguimiento_horas` | `integer` | Obligatorio (default: 24) | Horas después del primer seguimiento para el segundo. | `24` |\n| 27 | `intervalo_tercer_seguimiento_horas` | `integer` | Obligatorio (default: 48) | Horas después del segundo seguimiento para el tercero. | `48` |\n| 28 | `plazo_respuesta_primer_seguimiento` | `integer` | Obligatorio (default: 48) | Horas de plazo de respuesta después del primer mensaje. | `48` |\n| 29 | `plazo_respuesta_segundo_seguimiento` | `integer` | Obligatorio (default: 24) | Horas de plazo de respuesta después del segundo mensaje. | `24` |\n| 30 | `plazo_respuesta_tercer_seguimiento` | `integer` | Obligatorio (default: 48) | Horas de plazo de respuesta después del tercer mensaje. | `48` |\n\n---\n\n## Enlaces de pago\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 31 | `enlace_pago` | `string` | Obligatorio | URL directa para actualizar el método de pago. | `\"https://app.fitzone.com/pago/actualizar\"` |\n| 32 | `enlace_soporte` | `string` | Opcional | URL de soporte o ayuda para problemas con el pago. | `\"https://app.fitzone.com/soporte\"` |\n\n---\n\n## Datos del gimnasio\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 33 | `gimnasio_id` | `string` | Obligatorio | Identificador único del gimnasio. | `\"GYM-001\"` |\n| 34 | `gimnasio_nombre` | `string` | Obligatorio | Nombre comercial del gimnasio. | `\"FitZone Centro\"` |\n| 35 | `gimnasio_telefono` | `string` | Opcional | Teléfono del gimnasio. | `\"+54 11 4444-5678\"` |\n\n---\n\n## Datos de notificación\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 36 | `responsable_cobranza` | `string` | Obligatorio | Nombre del responsable de cobranzas o departamento. | `\"Departamento de Cobranzas\"` |\n| 37 | `email_responsable` | `string` | Obligatorio | Correo electrónico del responsable. | `\"cobranzas@fitzone.com\"` |\n| 38 | `canal_notificacion_equipo` | `string` (enum) | Opcional | Canal para notificar al equipo. Valores: `email`, `app`. Default: `email`. | `\"email\"` |\n\n---\n\n## Datos de tiempos\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 39 | `tiempo_fecha_actual` | `string` (ISO 8601) | Obligatorio | Fecha y hora actual del sistema. | `\"2026-08-23T14:30:00Z\"` |\n| 40 | `horario_atencion_inicio` | `string` (HH:MM) | Opcional | Hora de inicio del horario de atención. Default: `\"08:00\"`. | `\"08:00\"` |\n| 41 | `horario_atencion_fin` | `string` (HH:MM) | Opcional | Hora de fin del horario de atención. Default: `\"20:00\"`. | `\"20:00\"` |\n\n---\n\n## Campos calculados por el agente\n\nEstos campos **no se reciben de entrada**; el agente los genera internamente.\n\n| # | Nombre del campo | Tipo de dato | Generado en | Descripción |\n|---|---|---|---|---|\n| 42 | `caso_id` | `string` | Paso 1 | ID único del caso. Formato: `CASE-YYYYMMDD-HHMMSS-XXX`. |\n| 43 | `fecha_hora_deteccion_fallo_ajustada` | `string` (ISO 8601) | Paso 1 | Fecha de detección ajustada al horario si aplica. |\n| 44 | `fecha_limite_suspension` | `string` (ISO 8601) | Paso 1 | Fecha límite antes de suspender el acceso. |\n| 45 | `momento_envio_seguimiento_calculado` | `string` (ISO 8601) | Paso 2 | Momento de envío calculado. |\n| 46 | `momento_envio_seguimiento_ajustado` | `string` (ISO 8601) | Paso 2 | Momento de envío ajustado al horario. |\n| 47 | `ajuste_horario` | `boolean` | Paso 2 | `true` si se ajustó el horario. |\n| 48 | `razon_ajuste` | `string` o `null` | Paso 2 | Razón del ajuste. |\n| 49 | `fecha_hora_inicio_espera` | `string` (ISO 8601) | Paso 2 | Marca de inicio de espera. |\n| 50 | `seguimientos_enviados` | `integer` | Paso 4 | Número de mensajes de seguimiento enviados. |\n| 51 | `fecha_hora_envio_seguimiento` | `string` (ISO 8601) o `null` | Paso 4 | Marca de envío del último seguimiento. |\n| 52 | `seguimiento_numero` | `integer` | Paso 4 | Número del seguimiento actual (1, 2 o 3). |\n| 53 | `respuesta_miembro` | `string` (enum) o `null` | Paso 5 | Clasificación de la respuesta. |\n| 54 | `texto_respuesta` | `string` o `null` | Paso 5 | Texto exacto de la respuesta. |\n| 55 | `fecha_hora_respuesta` | `string` (ISO 8601) o `null` | Paso 5 | Marca de la respuesta. |\n| 56 | `verificacion_pago` | `object` o `null` | Paso 6 | Resultado de verificación de pago. |\n| 57 | `estado_caso` | `string` (enum) | Paso 6 | Estado final del caso. |\n| 58 | `resultado` | `string` (enum) | Paso 6 | Resultado final. |\n| 59 | `fecha_hora_cierre` | `string` (ISO 8601) | Paso 6 | Marca de cierre del caso. |\n| 60 | `duracion_caso_horas` | `number` | Paso 6 | Duración total del caso en horas. |\n\n---\n\n## Validaciones de entrada\n\n| Campo | Regla | Acción si falla |\n|---|---|---|\n| `evento_id` | No vacío, formato válido | Terminar flujo sin ejecutar |\n| `contacto_id` | Debe seguir formato `CT-YYYY-XXXXX` | Terminar flujo sin ejecutar |\n| `evento_tipo` | Debe ser `pago_fallido` o `reintento_fallido` | Terminar flujo sin ejecutar |\n| `nombre_miembro` | No vacío, mínimo 2 caracteres | Terminar flujo sin ejecutar |\n| `telefono_miembro` | Entre 8 y 15 dígitos si `canal_preferido` es `whatsapp` o `sms` | Escalar a humano (C4.1) |\n| `email_miembro` | Requerido si `canal_preferido` es `email` | Escalar a humano (C4.1) |\n| `membresia_tipo` | Debe ser uno de los 4 valores válidos | Terminar flujo sin ejecutar |\n| `membresia_estado` | Debe ser `activa`, `en_riesgo` o `suspendida` | Terminar flujo sin ejecutar |\n| `pago_id` | No vacío, formato válido | Terminar flujo sin ejecutar |\n| `monto_pago` | Número positivo mayor a 0 | Terminar flujo sin ejecutar |\n| `moneda` | Código ISO 4217 válido | Usar `\"ARS\"` como valor por defecto |\n| `motivo_fallo` | Debe ser uno de los 7 valores válidos | Usar `\"desconocido\"` como valor por defecto |\n| `enlace_pago` | No vacío, formato URL válido | Usar URL genérica del gimnasio |\n| `gimnasio_id` | No vacío | Terminar flujo sin ejecutar |\n| `responsable_cobranza` | No vacío | Terminar flujo sin ejecutar |\n| `email_responsable` | Formato de email válido | Escalar a humano (C4.1) |\n\n---\n\n## Ejemplo de payload completo\n\n```json\n{\n  \"evento_id\": \"PAY-FAIL-20260823-143000-001\",\n  \"contacto_id\": \"CT-2026-00452\",\n  \"evento_tipo\": \"pago_fallido\",\n  \"fecha_hora_deteccion_fallo\": \"2026-08-23T14:30:00Z\",\n  \"nombre_miembro\": \"María López\",\n  \"telefono_miembro\": \"+54 11 5555-1234\",\n  \"email_miembro\": \"maria.lopez@email.com\",\n  \"canal_preferido\": \"whatsapp\",\n  \"membresia_tipo\": \"premium\",\n  \"membresia_estado\": \"activa\",\n  \"membresia_fecha_vencimiento\": \"2026-08-23\",\n  \"membresia_plan_duracion_dias\": 30,\n  \"pago_id\": \"PAY-20260823-001\",\n  \"monto_pago\": 15000,\n  \"moneda\": \"ARS\",\n  \"fecha_pago_original\": \"2026-08-23\",\n  \"motivo_fallo\": \"fondos_insuficientes\",\n  \"motivo_fallo_detalle\": \"Fondos insuficientes en la tarjeta terminada en 4532\",\n  \"metodo_pago_tipo\": \"tarjeta_credito\",\n  \"metodo_pago_ultimos_4\": \"4532\",\n  \"intentos_previos\": 0,\n  \"max_intentos_reintento\": 2,\n  \"reintento_automatico_habilitado\": true,\n  \"dias_limite_suspension\": 7,\n  \"intervalo_primer_seguimiento_horas\": 4,\n  \"intervalo_segundo_seguimiento_horas\": 24,\n  \"intervalo_tercer_seguimiento_horas\": 48,\n  \"plazo_respuesta_primer_seguimiento\": 48,\n  \"plazo_respuesta_segundo_seguimiento\": 24,\n  \"plazo_respuesta_tercer_seguimiento\": 48,\n  \"enlace_pago\": \"https://app.fitzone.com/pago/actualizar\",\n  \"enlace_soporte\": \"https://app.fitzone.com/soporte\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"gimnasio_telefono\": \"+54 11 4444-5678\",\n  \"responsable_cobranza\": \"Departamento de Cobranzas\",\n  \"email_responsable\": \"cobranzas@fitzone.com\",\n  \"canal_notificacion_equipo\": \"email\",\n  \"tiempo_fecha_actual\": \"2026-08-23T14:30:00Z\",\n  \"horario_atencion_inicio\": \"08:00\",\n  \"horario_atencion_fin\": \"20:00\"\n}\n```\n\n---\n*Plantilla generada para el nicho Gimnasios y Fitness — Agente Failed Payment Follow-up*\n"
              }
            ]
          }
        ]
      },
      {
        "id": "challenge-promotion",
        "carpeta": "challenge-promotion-agent",
        "nombre": "Challenge Promotion Agent",
        "icono": "🏆",
        "descripcion": "Promover challenges fitness y eventos especiales a miembros",
        "submodulos": [],
        "categoria": "general",
        "estructura": [
          {
            "id": "challenge-promotion-agent",
            "etiqueta": "",
            "es_plantilla": true,
            "nombre": "challenge-promotion-agent",
            "subcarpetas": [],
            "notas": [
              {
                "id": "challenge-promotion-agent/flujo.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "flujo.md",
                "contenido": "# Flujo de ejecución — Challenge Promotion Agent\n\nEste archivo documenta el flujo fijo de ejecución del agente. Cada campaña de marketing sigue estos 5 pasos **en orden**, sin pasos condicionales ni bifurcaciones.\n\n---\n\n## Paso 1 — Recepción del objetivo y tema de la campaña o pieza de contenido\n\nEl agente recibe el objetivo y tema de la campaña o pieza de contenido que se desea crear.\n\n**Acción del agente:**\n1. Recibir los datos según lo definido en `variables-entrada.md`.\n2. Registrar la marca de tiempo de recepción en `fecha_hora_recepcion`.\n3. Validar que el `campaign_id` tenga un formato válido.\n4. Verificar que el `objetivo_campana` esté definido y sea claro.\n5. Determinar el `tipo_campana` según la tabla de tipos.\n6. Verificar que los canales de publicación solicitados sean válidos.\n7. Verificar que no exista una campaña activa con el mismo tema en los últimos 30 días.\n8. Generar el `campaign_id` único si no fue proporcionado.\n\n**Tabla de objetivos válidos:**\n\n| Objetivo | Descripción | Métrica de éxito |\n|---|---|---|\n| `incrementar_asistencia` | Aumentar la asistencia a clases o el uso del gimnasio | +25% asistencia en 30 días |\n| `generar_inscripciones` | Captar nuevos miembros | +15 inscripciones en 30 días |\n| `retener_miembros` | Mantener a los miembros activos y comprometidos | -10% churn en 30 días |\n| `reactivar_inactivos` | Recuperar miembros que dejaron de asistir | +20 miembros reactivados en 30 días |\n| `promocionar_servicio` | Destacar un servicio específico (clase, entrenador, instalación) | +30% consultas sobre el servicio |\n| `generar_engagement` | Aumentar interacción en redes sociales | +50% likes/comentarios en 30 días |\n| `construir_comunidad` | Fortalecer el sentido de pertenencia | +20% participación en eventos |\n\n**Ejemplo de evento recibido:**\n\n```json\n{\n  \"campaign_id\": \"CAMP-20260823-001\",\n  \"evento_tipo\": \"solicitud_campana\",\n  \"fecha_hora_solicitud\": \"2026-08-23T10:00:00Z\",\n  \"solicitante\": \"Gerencia FitZone Centro\",\n  \"email_solicitante\": \"gerencia@fitzone.com\",\n  \"objetivo_campana\": \"incrementar_asistencia\",\n  \"tema_campana\": \"Reto 30 Días Spinning\",\n  \"descripcion_breve\": \"Campaña para aumentar la asistencia a clases de Spinning con un reto de 30 días. Incluir inscripción gratuita del reto y descuento en membresías nuevas.\",\n  \"tipo_campana\": \"reto\",\n  \"canales_objetivo\": [\"instagram\", \"whatsapp_broadcast\", \"email_newsletter\"],\n  \"fecha_inicio_deseada\": \"2026-09-01\",\n  \"fecha_fin_deseada\": \"2026-09-30\",\n  \"presupuesto_estimado\": 50000,\n  \"moneda\": \"ARS\",\n  \"publico_objetivo\": \"Miembros actuales y personas interesadas en spinning dentro del radio de 5km\",\n  \"tono_deseado\": \"Enérgico, motivador, competitivo\",\n  \"referencias\": [\"Campaña similar de SoulCycle\", \"Reto 21 días de Peloton\"],\n  \"restricciones\": [\"No mencionar precios menores a $8.000\", \"No usar fotos de modelos stock\", \"Incluir logo en todas las piezas\"],\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"guia_marca\": {\n    \"colores_principales\": [\"#FF4500\", \"#1A1A2E\", \"#FFFFFF\"],\n    \"tipografia_titulos\": \"Montserrat Bold\",\n    \"tipografia_cuerpo\": \"Open Sans\",\n    \"tono_marca\": \"Enérgico, cercano, sin ser informal\",\n    \"emojis_permitidos\": true,\n    \"hashtags_fijos\": [\"#FitZone\", \"#FitZoneCentro\", \"#TuGimnasio\"]\n  },\n  \"responsable_aprobacion\": \"Carlos Pérez (Gerente de Marketing)\",\n  \"email_responsable\": \"carlos.perez.marketing@fitzone.com\",\n  \"canal_notificacion\": \"email\",\n  \"tiempo_fecha_actual\": \"2026-08-23T10:00:00Z\"\n}\n```\n\n---\n\n## Paso 2 — Generación de propuesta de contenido (texto/calendario) basada en el objetivo definido\n\nEl agente genera una propuesta completa de contenido basada en el objetivo, tema y canales definidos.\n\n**Acción del agente:**\n1. Analizar el `objetivo_campana` y el `tema_campana`.\n2. Determinar la cantidad de piezas necesarias según los canales y la duración de la campaña.\n3. Generar el calendario de publicación con fechas, horarios y canales.\n4. Para cada pieza, generar:\n   - Texto principal (copy).\n   - Texto alternativo (variante A/B si aplica).\n   - Sugerencia visual (descripción de imagen/video).\n   - Hashtags (para Instagram/TikTok).\n   - CTA (llamada a la acción).\n5. Respetar las restricciones y la guía de marca.\n6. Incluir métricas sugeridas para medir el éxito.\n\n**Tabla de cantidad de piezas por tipo de campaña:**\n\n| Tipo de campaña | Duración estándar | Piezas totales | Frecuencia |\n|---|---|---|---|\n| `reto` | 30 días | 12-16 piezas | 3-4/semana |\n| `promo` | 7-14 días | 6-8 piezas | 4-5/semana |\n| `evento` | 14-30 días | 8-10 piezas | 2-3/semana |\n| `testimonio` | 14 días | 6-8 piezas | 3-4/semana |\n| `contenido` | 30 días | 12-16 piezas | 3-4/semana |\n| `referido` | 30 días | 8-10 piezas | 2-3/semana |\n| `reactivacion` | 14 días | 6-8 piezas | 3-4/semana |\n| `estacional` | Variable | Variable | Variable |\n\n**Ejemplo de calendario generado (Reto 30 Días Spinning):**\n\n```json\n{\n  \"calendario\": [\n    {\n      \"pieza_id\": \"PIEZA-001\",\n      \"fecha_programacion\": \"2026-09-01T09:00:00Z\",\n      \"canal\": \"instagram\",\n      \"formato\": \"carrusel\",\n      \"titulo\": \"Lanzamiento Reto 30 Días\",\n      \"semana\": 1\n    },\n    {\n      \"pieza_id\": \"PIEZA-002\",\n      \"fecha_programacion\": \"2026-09-02T10:00:00Z\",\n      \"canal\": \"whatsapp_broadcast\",\n      \"formato\": \"mensaje_texto\",\n      \"titulo\": \"Invitación directa al reto\",\n      \"semana\": 1\n    },\n    {\n      \"pieza_id\": \"PIEZA-003\",\n      \"fecha_programacion\": \"2026-09-03T08:00:00Z\",\n      \"canal\": \"email_newsletter\",\n      \"formato\": \"email_html\",\n      \"titulo\": \"Newsletter lanzamiento reto\",\n      \"semana\": 1\n    },\n    {\n      \"pieza_id\": \"PIEZA-004\",\n      \"fecha_programacion\": \"2026-09-08T09:00:00Z\",\n      \"canal\": \"instagram\",\n      \"formato\": \"reel\",\n      \"titulo\": \"Semana 2: Testimonio participante\",\n      \"semana\": 2\n    },\n    {\n      \"pieza_id\": \"PIEZA-005\",\n      \"fecha_programacion\": \"2026-09-15T09:00:00Z\",\n      \"canal\": \"instagram\",\n      \"formato\": \"carrusel\",\n      \"titulo\": \"Semana 3: Desafío especial\",\n      \"semana\": 3\n    },\n    {\n      \"pieza_id\": \"PIEZA-006\",\n      \"fecha_programacion\": \"2026-09-22T09:00:00Z\",\n      \"canal\": \"instagram\",\n      \"formato\": \"carrusel\",\n      \"titulo\": \"Semana 4: Cierre y resultados\",\n      \"semana\": 4\n    }\n  ]\n}\n```\n\n**Ejemplo de pieza generada (PIEZA-001 — Instagram Carrusel):**\n\n```json\n{\n  \"pieza_id\": \"PIEZA-001\",\n  \"canal\": \"instagram\",\n  \"formato\": \"carrusel\",\n  \"fecha_programacion\": \"2026-09-01T09:00:00Z\",\n  \"texto_principal\": \"¡Empezamos el Reto 30 Días Spinning! 🚴‍♂️\\n\\n¿Listo para superar tus límites?\\n\\n📅 Del 1 al 30 de septiembre\\n🎯 30 clases de spinning en 30 días\\n🏆 Premios para los que completen el reto\\n\\nInscribite gratis en recepción o por este canal.\\n\\n💪 Cuota especial: $8.500/mes con acceso ilimitado a spinning.\\n\\n#RetoSpinning #FitZone #FitZoneCentro #TuGimnasio #Spinning #Fitness #RetosFitness\",\n  \"texto_alternativo\": \"Arranca el Reto 30 Días Spinning 🚴‍♂️\\n\\nUn desafío para vos: 30 clases en 30 días.\\n\\n¿Te animás?\\n\\nInscripción gratuita en recepción.\\nMembresía especial: $8.500/mes.\\n\\n#RetoSpinning #FitZone #FitZoneCentro\",\n  \"sugerencia_visual\": \"Foto de grupo en clase de spinning con luces de neón. Texto superpuesto: 'RETO 30 DÍAS' en Montserrat Bold, color #FF4500. Logo FitZone en esquina inferior derecha. Colores: #FF4500, #1A1A2E, #FFFFFF.\",\n  \"hashtags\": [\"#RetoSpinning\", \"#FitZone\", \"#FitZoneCentro\", \"#TuGimnasio\", \"#Spinning\", \"#Fitness\", \"#RetosFitness\"],\n  \"cta\": \"Inscribite gratis en recepción\",\n  \"metricas_sugeridas\": {\n    \"alcance_objetivo\": 5000,\n    \"likes_objetivo\": 200,\n    \"comentarios_objetivo\": 50,\n    \"guardados_objetivo\": 100,\n    \"cliques_enlace_objetivo\": 30\n  }\n}\n```\n\n---\n\n## Paso 3 — Ajuste del tono y formato según la guía de marca del negocio\n\nEl agente ajusta la propuesta generada en el paso 2 según la guía de marca del gimnasio.\n\n**Acción del agente:**\n1. Verificar que cada pieza use los colores principales de la guía de marca.\n2. Verificar que la tipografía de los textos coincida con la configurada.\n3. Ajustar el tono del copy según `tono_marca`.\n4. Verificar que los hashtags fijos estén incluidos en cada pieza.\n5. Verificar que el logo aparezca en las descripciones de imagen.\n6. Verificar que no se usen emojis si `emojis_permitidos` es `false`.\n7. Verificar que no se incumplan las `restricciones` definidas.\n8. Generar el informe de consistencia de marca.\n\n**Tabla de verificación de marca:**\n\n| Elemento | Regla | Estado |\n|---|---|---|\n| Colores principales | Usar solo `#FF4500`, `#1A1A2E`, `#FFFFFF` | ✅/❌ |\n| Tipografía títulos | Montserrat Bold en todas las piezas | ✅/❌ |\n| Tipografía cuerpo | Open Sans en todas las piezas | ✅/❌ |\n| Tono | Enérgico, cercano, sin ser informal | ✅/❌ |\n| Hashtags fijos | Incluir #FitZone, #FitZoneCentro, #TuGimnasio | ✅/❌ |\n| Logo | Mencionado en descripción de imagen | ✅/❌ |\n| Emojis | Permitidos según configuración | ✅/❌ |\n| Restricciones | No mencionar precios menores a $8.000 | ✅/❌ |\n| Restricciones | No usar fotos de modelos stock | ✅/❌ |\n\n**Ejemplo de informe de consistencia:**\n\n```json\n{\n  \"informe_marca\": {\n    \"total_piezas_verificadas\": 6,\n    \"piezas_conforme\": 6,\n    \"piezas_inconforme\": 0,\n    \"ajustes_realizados\": [\n      {\n        \"pieza_id\": \"PIEZA-001\",\n        \"ajuste\": \"Se agregó hashtag #TuGimnasio que faltaba en la versión inicial\"\n      }\n    ],\n    \"restricciones_verificadas\": [\n      \"No mencionar precios menores a $8.000: ✅ Cumplido\",\n      \"No usar fotos de modelos stock: ✅ Cumplido\",\n      \"Incluir logo en todas las piezas: ✅ Cumplido\"\n    ],\n    \"tono_verificado\": \"Enérgico, cercano, sin ser informal: ✅ Consistente\"\n  }\n}\n```\n\n---\n\n## Paso 4 — Entrega de la propuesta al responsable humano para aprobación\n\nEl agente entrega la propuesta completa al responsable de aprobación y espera su respuesta.\n\n**Acción del agente:**\n1. Compilar la propuesta completa: calendario + todas las piezas + informe de marca.\n2. Generar el mensaje de entrega usando la plantilla de `formato-salida.md` → sección 1.\n3. Enviar la propuesta al `responsable_aprobacion` por el `canal_notificacion`.\n4. Si el envío falla, reintentar una sola vez después de 30 segundos.\n5. Si el segundo intento falla, registrar el error y activar escalamiento (ver `reglas-escalamiento.md` → C3.1).\n6. Registrar la marca de tiempo de envío en `fecha_hora_envio_propuesta`.\n7. Actualizar el estado de la campaña a `pendiente_aprobacion`.\n8. Iniciar el contador de plazo de respuesta: **48 horas**.\n9. Calcular el `momento_recordatorio_aprobacion`: `fecha_hora_envio_propuesta` + 24 horas.\n\n**Plantilla de entrega de propuesta (email):**\n\n**Asunto:** Propuesta de campaña lista para revisión — [tema_campana] | [gimnasio_nombre]\n\n```\nHola [responsable_nombre],\n\nLa propuesta de campaña \"[tema_campana]\" está lista para tu revisión y aprobación.\n\n📋 Detalles de la campaña:\n- Objetivo: [objetivo_campana]\n- Tipo: [tipo_campana]\n- Período: [fecha_inicio] – [fecha_fin]\n- Canales: [canales_objetivo]\n- Presupuesto estimado: [presupuesto_formateado]\n\n📊 Resumen:\n- Total de piezas: [total_piezas]\n- Canales cubiertos: [total_canales]\n- Consistencia de marca: [piezas_conforme]/[total_piezas] conformes\n\n📎 Propuesta completa adjunta en este email.\n\nPara aprobar, respondé \"APROBADO\" o \"RECHAZADO\" con los comentarios.\nSi necesitás cambios, indicá los puntos a ajustar.\n\nPlazo de respuesta: 48 horas.\n— [gimnasio_nombre]\n```\n\n**Plantilla de entrega de propuesta (WhatsApp):**\n\n```\n📋 Propuesta de campaña lista\n\nObjetivo: [objetivo_campana]\nTema: [tema_campana]\nPeríodo: [fecha_inicio] – [fecha_fin]\nPiezas: [total_piezas]\nCanales: [canales_objetivo]\n\nEnvié los detalles por email. Revisá y aprobá cuando puedas.\n\nPlazo: 48 horas.\n— [gimnasio_nombre]\n```\n\n**Ejemplo renderizado de entrega (email):**\n\n```\nAsunto: Propuesta de campaña lista para revisión — Reto 30 Días Spinning | FitZone Centro\n\nHola Carlos Pérez,\n\nLa propuesta de campaña \"Reto 30 Días Spinning\" está lista para tu revisión y aprobación.\n\n📋 Detalles de la campaña:\n- Objetivo: Incrementar asistencia\n- Tipo: Reto\n- Período: 01/09/2026 – 30/09/2026\n- Canales: Instagram, WhatsApp Broadcast, Email Newsletter\n- Presupuesto estimado: $50.000 ARS\n\n📊 Resumen:\n- Total de piezas: 12\n- Canales cubiertos: 3\n- Consistencia de marca: 12/12 conformes\n\n📎 Propuesta completa adjunta en este email.\n\nPara aprobar, respondé \"APROBADO\" o \"RECHAZADO\" con los comentarios.\nSi necesitás cambios, indicá los puntos a ajustar.\n\nPlazo de respuesta: 48 horas.\n— FitZone Centro\n```\n\n**Ejemplo renderizado de entrega (WhatsApp):**\n\n```\n📋 Propuesta de campaña lista\n\nObjetivo: Incrementar asistencia\nTema: Reto 30 Días Spinning\nPeríodo: 01/09/2026 – 30/09/2026\nPiezas: 12\nCanales: Instagram, WhatsApp, Email\n\nEnvié los detalles por email. Revisá y aprobá cuando puedas.\n\nPlazo: 48 horas.\n— FitZone Centro\n```\n\n---\n\n## Paso 5 — Programación de la publicación/envío solo tras la aprobación explícita\n\nEl agente espera la aprobación del responsable y, solo después de recibirla, programa la publicación de las piezas.\n\n**Acción del agente:**\n1. Esperar la respuesta del responsable dentro del plazo de 48 horas.\n2. Si el responsable responde con \"APROBADO\" o similar:\n   - Registrar la aprobación con fecha y hora.\n   - Programar cada pieza según el calendario.\n   - Confirmar la programación al responsable.\n   - Actualizar el estado de la campaña a `programada`.\n3. Si el responsable responde con \"RECHAZADO\":\n   - Registrar el rechazo con los comentarios.\n   - Cerrar la campaña como `rechazada`.\n   - Notificar al solicitante.\n4. Si el responsable solicita cambios:\n   - Registrar los cambios solicitados.\n   - Generar una nueva versión de las piezas afectadas.\n   - Reenviar para aprobación (volver al paso 4).\n   - Incrementar el contador `revisiones_realizadas`.\n5. Si el plazo expira sin respuesta:\n   - Enviar un recordatorio (1 recordatorio máximo).\n   - Si no responde en 24 horas más, escalar a humano.\n6. Registrar la marca de tiempo de cierre en `fecha_hora_cierre`.\n\n**Tabla de estados de la campaña:**\n\n| Estado | Condición |\n|---|---|\n| `borrador` | Propuesta en generación |\n| `pendiente_aprobacion` | Propuesta enviada, esperando respuesta |\n| `aprobada` | Responsable aprobó la campaña |\n| `rechazada` | Responsable rechazó la campaña |\n| `con_cambios` | Responsable solicitó modificaciones |\n| `programada` | Piezas programadas para publicación |\n| `publicada` | Al menos 1 pieza fue publicada |\n| `completada` | Todas las piezas fueron publicadas |\n| `cancelada` | Campaña cancelada antes de publicar |\n\n**Plantilla de confirmación de programación (email):**\n\n**Asunto:** Campaña programada — [tema_campana] | [gimnasio_nombre]\n\n```\nHola [responsable_nombre],\n\nLa campaña \"[tema_campana]\" fue aprobada y programada exitosamente.\n\n📅 Inicio: [fecha_inicio]\n📅 Fin: [fecha_fin]\n📊 Total de piezas: [total_piezas]\n📋 Programadas: [piezas_programadas]\n\nPróxima publicación:\n- Pieza: [pieza_proxima_titulo]\n- Canal: [pieza_proxima_canal]\n- Fecha: [pieza_proxima_fecha]\n\nCualquier modificación antes del inicio, avisame.\n— [gimnasio_nombre]\n```\n\n**Plantilla de confirmación de programación (WhatsApp):**\n\n```\n✅ Campaña \"[tema_campana]\" aprobada y programada\n\n📅 Inicio: [fecha_inicio]\n📊 Piezas: [total_piezas]\n📋 Próxima publicación: [pieza_proxima_titulo] el [pieza_proxima_fecha]\n\nSi necesitás cambios, avisame.\n— [gimnasio_nombre]\n```\n\n**Ejemplo renderizado de confirmación (WhatsApp):**\n\n```\n✅ Campaña \"Reto 30 Días Spinning\" aprobada y programada\n\n📅 Inicio: 01/09/2026\n📊 Piezas: 12\n📋 Próxima publicación: Lanzamiento Reto 30 Días el 01/09/2026 09:00\n\nSi necesitás cambios, avisame.\n— FitZone Centro\n```\n\n**Plantilla de solicitud de cambios (email):**\n\n**Asunto:** Cambios solicitados en campaña — [tema_campana] | [gimnasio_nombre]\n\n```\nHola [responsable_nombre],\n\nRecibí tus comentarios sobre la campaña \"[tema_campana]\".\n\n📝 Cambios solicitados:\n[lista_cambios]\n\nEstoy generando la versión actualizada. Te la envío para aprobación en las próximas horas.\n\n— [gimnasio_nombre]\n```\n\n**Plantilla de cierre por rechazo (email):**\n\n**Asunto:** Campaña rechazada — [tema_campana] | [gimnasio_nombre]\n\n```\nHola [responsable_nombre],\n\nLa campaña \"[tema_campana]\" fue rechazada según tu indicación.\n\n📝 Motivo del rechazo: [motivo_rechazo]\n\nLa campaña se cerró sin programar publicaciones. Si querés重新generar una nueva propuesta, avisame.\n— [gimnasio_nombre]\n```\n\n---\n*Plantilla generada para el nicho Gimnasios y Fitness — Agente Challenge Promotion Agent*\n"
              },
              {
                "id": "challenge-promotion-agent/formato-salida.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "formato-salida.md",
                "contenido": "# Formato de salida — Challenge Promotion Agent\n\n## Descripción\n\nDefine la estructura exacta del mensaje o registro que el agente debe producir en cada paso del flujo. Todos los campos deben tener valores concretos de ejemplo.\n\n---\n\n## 1. Propuesta de campaña completa\n\n**Cuándo se genera:** Paso 4 — Cuando la propuesta está lista para aprobación.\n\n**Estructura de la propuesta:**\n\n```\n📋 PROPUESTA DE CAMPAÑA — [tema_campana]\n\n═══════════════════════════════════════\nOBJETIVO\n═══════════════════════════════════════\nTipo: [tipo_campana]\nObjetivo: [objetivo_campana]\nPúblico: [publico_objetivo]\nPeríodo: [fecha_inicio] – [fecha_fin]\nCanales: [canales_objetivo]\nPresupuesto: [presupuesto_formateado]\n\n═══════════════════════════════════════\nCALENDARIO DE PUBLICACIÓN\n═══════════════════════════════════════\n[Tabla de calendario]\n\n═══════════════════════════════════════\nPIEZAS GENERADAS\n═══════════════════════════════════════\n[Detalle de cada pieza]\n\n═══════════════════════════════════════\nINFORME DE MARCA\n═══════════════════════════════════════\n[Resultado de verificación]\n```\n\n**Tabla de calendario:**\n\n| # | Fecha | Hora | Canal | Formato | Pieza | Semana |\n|---|---|---|---|---|---|---|\n| 1 | DD/MM/YYYY | HH:MM | [canal] | [formato] | [título] | [X] |\n\n**Detalle de cada pieza:**\n\n```\n📌 PIEZA [pieza_id] — [canal] [formato]\n\n📅 Programación: [fecha] [hora]\n📝 Texto principal:\n[texto_principal]\n\n📝 Texto alternativo:\n[texto_alternativo]\n\n🖼️ Sugerencia visual:\n[sugerencia_visual]\n\n#️⃣ Hashtags:\n[hashtags]\n\n👆 CTA:\n[cta]\n\n📊 Métricas objetivo:\n- Alcance: [alcance_objetivo]\n- Likes: [likes_objetivo]\n- Comentarios: [comentarios_objetivo]\n- Guardados: [guardados_objetivo]\n```\n\n**Ejemplo renderizado de propuesta completa:**\n\n```\n📋 PROPUESTA DE CAMPAÑA — Reto 30 Días Spinning\n\n═══════════════════════════════════════\nOBJETIVO\n═══════════════════════════════════════\nTipo: Reto\nObjetivo: Incrementar asistencia\nPúblico: Miembros actuales y personas interesadas en spinning dentro del radio de 5km\nPeríodo: 01/09/2026 – 30/09/2026\nCanales: Instagram, WhatsApp Broadcast, Email Newsletter\nPresupuesto: $50.000 ARS\n\n═══════════════════════════════════════\nCALENDARIO DE PUBLICACIÓN\n═══════════════════════════════════════\n| #  | Fecha       | Hora  | Canal              | Formato      | Pieza                          | Sem |\n|----|-------------|-------|--------------------|--------------|--------------------------------|-----|\n| 1  | 01/09/2026  | 09:00 | Instagram          | Carrusel     | Lanzamiento Reto 30 Días       | 1   |\n| 2  | 02/09/2026  | 10:00 | WhatsApp Broadcast | Mensaje      | Invitación directa al reto     | 1   |\n| 3  | 03/09/2026  | 08:00 | Email Newsletter   | Email HTML   | Newsletter lanzamiento reto    | 1   |\n| 4  | 08/09/2026  | 09:00 | Instagram          | Reel         | Testimonio participante        | 2   |\n| 5  | 15/09/2026  | 09:00 | Instagram          | Carrusel     | Desafío especial semana 3      | 3   |\n| 6  | 22/09/2026  | 09:00 | Instagram          | Carrusel     | Cierre y resultados            | 4   |\n\n═══════════════════════════════════════\nPIEZAS GENERADAS\n═══════════════════════════════════════\n\n📌 PIEZA PIEZA-001 — Instagram Carrusel\n\n📅 Programación: 01/09/2026 09:00\n📝 Texto principal:\n¡Empezamos el Reto 30 Días Spinning! 🚴‍♂️\n\n¿Listo para superar tus límites?\n\n📅 Del 1 al 30 de septiembre\n🎯 30 clases de spinning en 30 días\n🏆 Premios para los que completen el reto\n\nInscribite gratis en recepción o por este canal.\n\n💪 Cuota especial: $8.500/mes con acceso ilimitado a spinning.\n\n#RetoSpinning #FitZone #FitZoneCentro #TuGimnasio #Spinning #Fitness #RetosFitness\n\n📝 Texto alternativo:\nArranca el Reto 30 Días Spinning 🚴‍♂️\n\nUn desafío para vos: 30 clases en 30 días.\n\n¿Te animás?\n\nInscripción gratuita en recepción.\nMembresía especial: $8.500/mes.\n\n#RetoSpinning #FitZone #FitZoneCentro\n\n🖼️ Sugerencia visual:\nFoto de grupo en clase de spinning con luces de neón. Texto superpuesto: 'RETO 30 DÍAS' en Montserrat Bold, color #FF4500. Logo FitZone en esquina inferior derecha. Colores: #FF4500, #1A1A2E, #FFFFFF.\n\n#️⃣ Hashtags:\n#RetoSpinning #FitZone #FitZoneCentro #TuGimnasio #Spinning #Fitness #RetosFitness\n\n👆 CTA:\nInscribite gratis en recepción\n\n📊 Métricas objetivo:\n- Alcance: 5.000\n- Likes: 200\n- Comentarios: 50\n- Guardados: 100\n- Cliques enlace: 30\n\n═══════════════════════════════════════\nINFORME DE MARCA\n═══════════════════════════════════════\nTotal piezas verificadas: 6\nPiezas conformes: 6\nPiezas inconformes: 0\n\nAjustes realizados:\n- PIEZA-001: Se agregó hashtag #TuGimnasio que faltaba\n\nRestricciones verificadas:\n✅ No mencionar precios menores a $8.000\n✅ No usar fotos de modelos stock\n✅ Incluir logo en todas las piezas\n\nTono verificado: ✅ Enérgico, cercano, sin ser informal\n```\n\n---\n\n## 2. Mensaje de entrega de propuesta\n\n**Cuándo se genera:** Paso 4 — Cuando la propuesta está lista para enviarse al responsable.\n\n**Plantilla (email):**\n\n**Asunto:** Propuesta de campaña lista para revisión — [tema_campana] | [gimnasio_nombre]\n\n```\nHola [responsable_nombre],\n\nLa propuesta de campaña \"[tema_campana]\" está lista para tu revisión y aprobación.\n\n📋 Detalles de la campaña:\n- Objetivo: [objetivo_campana]\n- Tipo: [tipo_campana]\n- Período: [fecha_inicio] – [fecha_fin]\n- Canales: [canales_objetivo]\n- Presupuesto estimado: [presupuesto_formateado]\n\n📊 Resumen:\n- Total de piezas: [total_piezas]\n- Canales cubiertos: [total_canales]\n- Consistencia de marca: [piezas_conforme]/[total_piezas] conformes\n\n📎 Propuesta completa adjunta en este email.\n\nPara aprobar, respondé \"APROBADO\" o \"RECHAZADO\" con los comentarios.\nSi necesitás cambios, indicá los puntos a ajustar.\n\nPlazo de respuesta: 48 horas.\n— [gimnasio_nombre]\n```\n\n**Plantilla (WhatsApp):**\n\n```\n📋 Propuesta de campaña lista\n\nObjetivo: [objetivo_campana]\nTema: [tema_campana]\nPeríodo: [fecha_inicio] – [fecha_fin]\nPiezas: [total_piezas]\nCanales: [canales_objetivo]\n\nEnvié los detalles por email. Revisá y aprobá cuando puedas.\n\nPlazo: 48 horas.\n— [gimnasio_nombre]\n```\n\n**Ejemplo renderizado (WhatsApp):**\n\n```\n📋 Propuesta de campaña lista\n\nObjetivo: Incrementar asistencia\nTema: Reto 30 Días Spinning\nPeríodo: 01/09/2026 – 30/09/2026\nPiezas: 6\nCanales: Instagram, WhatsApp, Email\n\nEnvié los detalles por email. Revisá y aprobá cuando puedas.\n\nPlazo: 48 horas.\n— FitZone Centro\n```\n\n---\n\n## 3. Confirmación de programación\n\n**Cuándo se genera:** Paso 5 — Después de la aprobación.\n\n**Plantilla (email):**\n\n**Asunto:** Campaña programada — [tema_campana] | [gimnasio_nombre]\n\n```\nHola [responsable_nombre],\n\nLa campaña \"[tema_campana]\" fue aprobada y programada exitosamente.\n\n📅 Inicio: [fecha_inicio]\n📅 Fin: [fecha_fin]\n📊 Total de piezas: [total_piezas]\n📋 Programadas: [piezas_programadas]\n\nPróxima publicación:\n- Pieza: [pieza_proxima_titulo]\n- Canal: [pieza_proxima_canal]\n- Fecha: [pieza_proxima_fecha]\n\nCualquier modificación antes del inicio, avisame.\n— [gimnasio_nombre]\n```\n\n**Plantilla (WhatsApp):**\n\n```\n✅ Campaña \"[tema_campana]\" aprobada y programada\n\n📅 Inicio: [fecha_inicio]\n📊 Piezas: [total_piezas]\n📋 Próxima publicación: [pieza_proxima_titulo] el [pieza_proxima_fecha]\n\nSi necesitás cambios, avisame.\n— [gimnasio_nombre]\n```\n\n**Ejemplo renderizado (WhatsApp):**\n\n```\n✅ Campaña \"Reto 30 Días Spinning\" aprobada y programada\n\n📅 Inicio: 01/09/2026\n📊 Piezas: 6\n📋 Próxima publicación: Lanzamiento Reto 30 Días el 01/09/2026 09:00\n\nSi necesitás cambios, avisame.\n— FitZone Centro\n```\n\n---\n\n## 4. Registro de acción (JSON)\n\n**Cuándo se genera:** En cada paso del flujo, como registro interno.\n\n**Estructura:**\n\n```json\n{\n  \"registro_id\": \"CPA-REG-[timestamp]\",\n  \"agente\": \"challenge-promotion-agent\",\n  \"campaign_id\": \"[campaign_id]\",\n  \"solicitante\": \"[solicitante]\",\n  \"gimnasio_id\": \"[gimnasio_id]\",\n  \"gimnasio_nombre\": \"[gimnasio_nombre]\",\n  \"tema_campana\": \"[tema_campana]\",\n  \"tipo_campana\": \"[tipo_campana]\",\n  \"objetivo_campana\": \"[objetivo_campana]\",\n  \"canales_objetivo\": \"[canales_objetivo]\",\n  \"fecha_inicio_deseada\": \"[fecha_inicio_deseada]\",\n  \"fecha_fin_deseada\": \"[fecha_fin_deseada]\",\n  \"total_piezas\": \"[total_piezas]\",\n  \"total_canales\": \"[total_canales]\",\n  \"piezas_conforme_marca\": \"[piezas_conforme]\",\n  \"fecha_hora_envio_propuesta\": \"[fecha_hora_envio_propuesta]\",\n  \"fecha_hora_respuesta\": \"[fecha_hora_respuesta]\",\n  \"respuesta_aprobacion\": \"[respuesta_aprobacion]\",\n  \"texto_respuesta\": \"[texto_respuesta]\",\n  \"revisiones_realizadas\": \"[revisiones_realizadas]\",\n  \"estado_campana\": \"[estado_campana]\",\n  \"fecha_hora_cierre\": \"[fecha_hora_cierre]\",\n  \"duracion_proceso_horas\": \"[duracion_proceso_horas]\",\n  \"resultado\": \"[resultado]\",\n  \"timestamp_registro\": \"[tiempo_fecha_actual]\"\n}\n```\n\n**Ejemplo de registro completo (campaña aprobada y programada):**\n\n```json\n{\n  \"registro_id\": \"CPA-REG-20260825100000\",\n  \"agente\": \"challenge-promotion-agent\",\n  \"campaign_id\": \"CAMP-20260823-001\",\n  \"solicitante\": \"Gerencia FitZone Centro\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"tema_campana\": \"Reto 30 Días Spinning\",\n  \"tipo_campana\": \"reto\",\n  \"objetivo_campana\": \"incrementar_asistencia\",\n  \"canales_objetivo\": [\"instagram\", \"whatsapp_broadcast\", \"email_newsletter\"],\n  \"fecha_inicio_deseada\": \"2026-09-01\",\n  \"fecha_fin_deseada\": \"2026-09-30\",\n  \"total_piezas\": 6,\n  \"total_canales\": 3,\n  \"piezas_conforme_marca\": 6,\n  \"fecha_hora_envio_propuesta\": \"2026-08-23T14:00:00Z\",\n  \"fecha_hora_respuesta\": \"2026-08-24T10:00:00Z\",\n  \"respuesta_aprobacion\": \"aprobada\",\n  \"texto_respuesta\": \"APROBADO. Excelente propuesta, proceed con la programación.\",\n  \"revisiones_realizadas\": 0,\n  \"estado_campana\": \"programada\",\n  \"fecha_hora_cierre\": \"2026-08-24T10:05:00Z\",\n  \"duracion_proceso_horas\": 48.08,\n  \"resultado\": \"aprobada_programada\",\n  \"timestamp_registro\": \"2026-08-24T10:05:00Z\"\n}\n```\n\n**Ejemplo de registro completo (campaña con cambios solicitados):**\n\n```json\n{\n  \"registro_id\": \"CPA-REG-20260825100001\",\n  \"agente\": \"challenge-promotion-agent\",\n  \"campaign_id\": \"CAMP-20260823-002\",\n  \"solicitante\": \"Gerencia FitZone Centro\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"tema_campana\": \"Promo Verano 2x1\",\n  \"tipo_campana\": \"promo\",\n  \"objetivo_campana\": \"generar_inscripciones\",\n  \"canales_objetivo\": [\"instagram\", \"facebook\", \"whatsapp_broadcast\"],\n  \"fecha_inicio_deseada\": \"2026-12-01\",\n  \"fecha_fin_deseada\": \"2026-12-31\",\n  \"total_piezas\": 8,\n  \"total_canales\": 3,\n  \"piezas_conforme_marca\": 8,\n  \"fecha_hora_envio_propuesta\": \"2026-08-23T14:00:00Z\",\n  \"fecha_hora_respuesta\": \"2026-08-24T10:00:00Z\",\n  \"respuesta_aprobacion\": \"con_cambios\",\n  \"texto_respuesta\": \"Me gusta la idea pero cambiá el tono a más relajado y agregá una pieza de Facebook Evento para la inauguración.\",\n  \"revisiones_realizadas\": 1,\n  \"estado_campana\": \"con_cambios\",\n  \"fecha_hora_cierre\": null,\n  \"duracion_proceso_horas\": null,\n  \"resultado\": \"pendiente_revision\",\n  \"timestamp_registro\": \"2026-08-24T10:00:00Z\"\n}\n```\n\n---\n\n## 5. Registro de escalamiento\n\n**Cuándo se genera:** Cuando el agente escala el caso a un humano.\n\n**Estructura:**\n\n```json\n{\n  \"escalamiento_id\": \"CPA-ESC-[timestamp]\",\n  \"agente\": \"challenge-promotion-agent\",\n  \"gimnasio_id\": \"[gimnasio_id]\",\n  \"gimnasio_nombre\": \"[gimnasio_nombre]\",\n  \"campaign_id\": \"[campaign_id]\",\n  \"tema_campana\": \"[tema_campana]\",\n  \"solicitante\": \"[solicitante]\",\n  \"motivo_escalamiento\": \"[motivo_enum]\",\n  \"descripcion_caso\": \"[texto_descriptivo]\",\n  \"urgencia\": \"[alta | media | baja]\",\n  \"asignado_a\": \"[nombre_humano_asignado]\",\n  \"timestamp_escalamiento\": \"[tiempo_fecha_actual]\",\n  \"datos_contexto\": {\n    \"tipo_campana\": \"[tipo_campana]\",\n    \"total_piezas\": \"[total_piezas]\",\n    \"revisiones_realizadas\": \"[revisiones_realizadas]\",\n    \"estado_campana\": \"[estado_campana]\"\n  }\n}\n```\n\n**Ejemplo de registro de escalamiento:**\n\n```json\n{\n  \"escalamiento_id\": \"CPA-ESC-20260826100000\",\n  \"agente\": \"challenge-promotion-agent\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"campaign_id\": \"CAMP-20260823-002\",\n  \"tema_campana\": \"Promo Verano 2x1\",\n  \"solicitante\": \"Gerencia FitZone Centro\",\n  \"motivo_escalamiento\": \"maximo_revisiones_alcanzado\",\n  \"descripcion_caso\": \"La campaña 'Promo Verano 2x1' lleva 3 rondas de revisión sin aprobación. El responsable solicitó cambios en cada ronda y la última solicitud requiere un cambio de tono que afecta la identidad de la marca. Se necesita intervención directa para definir la dirección final.\",\n  \"urgencia\": \"media\",\n  \"asignado_a\": \"Gerencia FitZone Centro\",\n  \"timestamp_escalamiento\": \"2026-08-26T10:00:00Z\",\n  \"datos_contexto\": {\n    \"tipo_campana\": \"promo\",\n    \"total_piezas\": 8,\n    \"revisiones_realizadas\": 3,\n    \"estado_campana\": \"con_cambios\"\n  }\n}\n```\n\n---\n\n## 6. Formato de fechas\n\n| Campo | Formato | Ejemplo |\n|---|---|---|\n| Fecha de campaña | `DD/MM/YYYY` | `\"01/09/2026\"` |\n| Período | `DD/MM/YYYY – DD/MM/YYYY` | `\"01/09/2026 – 30/09/2026\"` |\n| Presupuesto | `$[monto] [moneda]` | `\"$50.000 ARS\"` |\n| Fecha ISO 8601 | `YYYY-MM-DDTHH:MM:SS` | `\"2026-08-23T10:00:00Z\"` |\n| Hora | `HH:MM` | `\"09:00\"` |\n\n---\n*Plantilla generada para el nicho Gimnasios y Fitness — Agente Challenge Promotion Agent*\n"
              },
              {
                "id": "challenge-promotion-agent/prompt-sistema.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "prompt-sistema.md",
                "contenido": "# Prompt del Sistema — Challenge Promotion Agent\n\n## Identidad\nEres **Challenge Promotion Agent**, un asistente automatizado de **[Nombre del Gimnasio]** especializado en planificar y generar contenido o campañas de marketing. Tu función es ejecutar el flujo de 5 pasos exactos que se define a continuación.\n\n## Tono de comunicación\n- Enérgico y motivador — refleja la energía del fitness\n- Orientado a resultados — cada pieza tiene un objetivo claro y medible\n- Consistente con la marca — se adapta a la guía de marca del gimnasio\n- Visual — las propuestas incluyen sugerencias de imágenes, colores y formato\n\n## Flujo de ejecución\n\nEl agente ejecuta estos 5 pasos en orden, sin bifurcaciones ni condicionales:\n\n### Paso 1: Recibe el objetivo y tema de la campaña o pieza de contenido\nEl agente recibe los datos de la campaña: objetivo, tema, canales, público objetivo, presupuesto y restricciones.\n\n### Paso 2: Genera una propuesta de contenido (texto/calendario) basada en el objetivo definido\nEl agente genera el calendario de publicación y las piezas de contenido con copy, hashtags, CTA y sugerencias visuales.\n\n### Paso 3: Ajusta el tono y formato según la guía de marca del negocio\nEl agente verifica colores, tipografía, tono, hashtags fijos, logo y restricciones de marca.\n\n### Paso 4: Entrega la propuesta al responsable humano para aprobación\nEl agente envía la propuesta completa al responsable y espera su respuesta (plazo: 48 horas).\n\n### Paso 5: Programa la publicación/envío solo tras la aprobación explícita\nEl agente espera la aprobación y, solo después de recibirla, programa cada pieza según el calendario.\n\n## Ejemplo de ejecución\n\n**Entrada:**\n```\ncampaign_id: \"CAMP-20260823-001\"\nobjetivo_campana: \"incrementar_asistencia\"\ntema_campana: \"Reto 30 Días Spinning\"\ntipo_campana: \"reto\"\ncanales_objetivo: [\"instagram\", \"whatsapp_broadcast\", \"email_newsletter\"]\n```\n\n**Ejecución paso a paso:**\n\n**Paso 1:** Recibe solicitud de campaña \"Reto 30 Días Spinning\" para incrementar asistencia a Spinning.\n\n**Paso 2:** Genera calendario de 12 piezas en 4 semanas: lanzamiento, testimonios, desafíos especiales, cierre.\n\n**Paso 3:** Verifica consistencia de marca: colores (#FF4500, #1A1A2E, #FFFFFF), tipografía (Montserrat Bold), hashtags fijos (#FitZone, #FitZoneCentro, #TuGimnasio).\n\n**Paso 4:** Envía propuesta a Carlos Pérez (Gerente de Marketing) por email. Espera aprobación.\n\n**Paso 5:** Tras aprobación, programa 12 piezas en Instagram, WhatsApp y Email.\n\n## Tipos de campaña (8 tipos)\n\n| Código | Tipo | Objetivo |\n|--------|------|----------|\n| `reto` | Reto fitness | Activar miembros y atraer nuevos |\n| `promo` | Promoción temporal | Generar inscripciones o ventas |\n| `evento` | Evento especial | Crear expectativa y asistencia |\n| `testimonio` | Testimonios | Construir prueba social |\n| `contenido` | Contenido de valor | Mantener engagement |\n| `referido` | Programa de referidos | Atraer nuevos miembros |\n| `reactivacion` | Reactivación | Recuperar miembros perdidos |\n| `estacional` | Campaña estacional | Aprovechar tendencias temporales |\n\n## Canales de publicación (7 canales)\n\n| Canal | Formato principal | Frecuencia |\n|-------|-------------------|------------|\n| `instagram` | Carrusel, Reel, Historia | 3-5 piezas/semana |\n| `facebook` | Post, Evento, Anuncio | 2-3 piezas/semana |\n| `whatsapp_broadcast` | Mensaje de texto | 1-2 envíos/semana |\n| `email_newsletter` | Email HTML | 1 envío/semana |\n| `tiktok` | Video corto | 3-5 piezas/semana |\n| `google_ads` | Anuncio de texto | Según presupuesto |\n| `sms` | Mensaje corto | 1-2 envíos/mes |\n\n## Restricciones\n1. Ejecutar solo los 5 pasos del flujo, sin agregar pasos adicionales\n2. Toda propuesta debe ser aprobada antes de publicar\n3. Respetar la guía de marca (colores, tipografía, tono)\n4. Incluir CTA en cada pieza\n5. No usar contenido engañoso ni exagerar beneficios\n6. Respetar frecuencias por canal — no saturar\n7. No publicar contenido polémico (política, religión)\n\n## Reglas de escalamiento\nEl agente escala a un humano en las siguientes situaciones:\n\n| Condición | Tiempo de respuesta |\n|-----------|---------------------|\n| Propuesta requiere aprobación de presupuesto mayor al configurado | 4 horas |\n| Contenido requiere revisión legal | 24 horas |\n| Solicitante solicita cambios sustanciales en la propuesta | 4 horas |\n| Error en el envío de la propuesta (3 intentos fallidos) | 1 hora |\n| Plazo de aprobación expirado sin respuesta | 2 horas |\n"
              },
              {
                "id": "challenge-promotion-agent/reglas-escalamiento.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "reglas-escalamiento.md",
                "contenido": "# Reglas de escalamiento — Challenge Promotion Agent\n\n## Descripción\n\nLista las condiciones exactas bajo las cuales el agente debe transferir el caso a un humano, sin dejar casos ambiguos. Cada condición tiene un identificador único, la situación que la activa, la acción a tomar y el plazo de respuesta.\n\n---\n\n## Condiciones de escalamiento\n\n### Categoría 1: Aprobación pendiente o rechazo\n\n| ID | Condición | Acción del agente | Urgencia | Plazo de respuesta |\n|----|-----------|-------------------|----------|-------------------|\n| C1.1 | El responsable no responde en el plazo de aprobación (48 horas) + recordatorio (24 horas) | Escalar a [gerencia] con la propuesta pendiente de aprobación. La campaña queda pausada hasta recibir instrucción. | Media | 24 horas |\n| C1.2 | El responsable rechaza la propuesta sin indicar motivos | Escalar a [gerencia] para solicitar clarificación. No generar nueva versión sin entender el motivo del rechazo. | Media | 24 horas |\n| C1.3 | El responsable solicita un cambio que contradice la guía de marca (ej. cambiar colores, tono opuesto) | Escalar a [gerencia] para verificar si se debe actualizar la guía de marca o rechazar el cambio. | Media | 24 horas |\n\n### Categoría 2: Revisiones excesivas\n\n| ID | Condición | Acción del agente | Urgencia | Plazo de respuesta |\n|----|-----------|-------------------|----------|-------------------|\n| C2.1 | Se alcanza el máximo de revisiones (3 rondas) sin aprobación | Escalar a [gerencia] para tomar una decisión final. La campaña queda pausada. | Media | 24 horas |\n| C2.2 | El responsable solicita cambios contradictorios entre rondas (ej. \"más formal\" → \"más informal\" → \"más formal\") | Escalar a [gerencia] para definir la dirección creativa. No generar nueva versión hasta resolver. | Media | 24 horas |\n| C2.3 | El responsable modifica los objetivos o canales después de 2 rondas de revisión | Escalar a [gerencia] para verificar si se debe reiniciar el proceso o ajustar la propuesta actual. | Baja | 48 horas |\n\n### Categoría 3: Problemas técnicos\n\n| ID | Condición | Acción del agente | Urgencia | Plazo de respuesta |\n|----|-----------|-------------------|----------|-------------------|\n| C3.1 | El envío de la propuesta falla dos veces consecutivas | Registrar el error. Escalar a [equipo técnico] para verificar el canal de notificación. La propuesta queda pendiente de envío. | Media | 24 horas |\n| C3.2 | El canal de notificación del responsable no está configurado o es inválido | Escalar a [equipo técnico] para verificar la configuración. Usar `email` como canal de respaldo si está disponible. | Baja | 48 horas |\n| C3.3 | Los canales objetivo incluyen un canal no soportado por el sistema | Informar al responsable que el canal no está disponible y ofrecer alternativas. No escalar a menos que el responsable insista. | Baja | 48 horas |\n\n### Categoría 4: Datos incompletos o inválidos\n\n| ID | Condición | Acción del agente | Urgencia | Plazo de respuesta |\n|----|-----------|-------------------|----------|-------------------|\n| C4.1 | El `email_responsable` no tiene formato válido | No enviar propuesta. Escalar a [equipo de datos] para verificar el registro del responsable. | Baja | 48 horas |\n| C4.2 | La `guia_marca` está incompleta (faltan campos obligatorios) | Usar guía de marca default del nicho. Informar al solicitante que faltan datos y solicitar la información completa. | Baja | 48 horas |\n| C4.3 | El `publico_objetivo` es ambiguo o no permite generar contenido específico | Solicitar clarificación al solicitante. Si no responde en 24 horas, usar un público genérico y escalar a [gerencia]. | Baja | 48 horas |\n\n### Categoría 5: Restricciones legales o éticas\n\n| ID | Condición | Acción del agente | Urgencia | Plazo de respuesta |\n|----|-----------|-------------------|----------|-------------------|\n| C5.1 | La propuesta contiene contenido que puede generar controversia (política, religión, competencia directa) | No entregar la propuesta. Escalar a [gerencia + asesoría legal] para revisión. | Alta | 4 horas |\n| C5.2 | El solicitante pide incluir testimonios falsos o resultados exagerados | Rechazar la solicitud. Escalar a [gerencia] con los detalles. No generar contenido engañoso. | Alta | 4 horas |\n| C5.3 | La campaña promete descuentos o beneficios que no están configurados en el sistema | Verificar con [departamento comercial] antes de incluir en la propuesta. Escalar si no se confirma. | Media | 24 horas |\n\n### Categoría 6: Errores del sistema\n\n| ID | Condición | Acción del agente | Urgencia | Plazo de respuesta |\n|----|-----------|-------------------|----------|-------------------|\n| C6.1 | El `campaign_id` recibido tiene formato inválido | No procesar. Escalar a [equipo técnico] para verificar el sistema de eventos. | Baja | 48 horas |\n| C6.2 | Hay más de 5 campañas activas simultáneamente para el mismo gimnasio | Informar al solicitante que hay muchas campañas activas. Escalar a [gerencia] para priorización. | Media | 24 horas |\n| C6.3 | La `fecha_inicio_deseada` es anterior a la fecha actual | No procesar. Informar al solicitante que la fecha ya pasó. Ofrecer reprogramar. | Baja | No requiere |\n\n---\n\n## Reglas de asignación de urgencia\n\n| Nivel | Color | Significado | Tiempo máximo de espera |\n|-------|-------|-------------|------------------------|\n| **Alta** | 🔴 | Requiere intervención humana inmediata para evitar problemas legales, éticos o de reputación | 4 horas |\n| **Media** | 🟡 | Requiere revisión humana para resolver la situación correctamente | 24 horas |\n| **Baja** | 🟢 | Informativo, requiere seguimiento pero no bloquea la campaña | 48 horas |\n\n---\n\n## Reglas de notificación por urgencia\n\n| Urgencia | Canal de notificación | Destinatario |\n|----------|----------------------|--------------|\n| **Alta** | WhatsApp + Email | [Responsable directo] + [Supervisor] |\n| **Media** | Email | [Responsable directo] |\n| **Baja** | Registro en sistema | [Equipo de datos] |\n\n---\n\n## Textos base para escalamiento\n\n### Al solicitante (cuando se escala):\n\n```\nHola [solicitante],\n\nLa campaña \"[tema_campana]\" requiere atención adicional de nuestro equipo.\n\nUn miembro de [gimnasio_nombre] te contactará para resolver la situación.\n\nMientras tanto, la campaña se mantiene pausada.\n— [gimnasio_nombre]\n```\n\n**Ejemplo:**\n\n```\nHola Gerencia FitZone Centro,\n\nLa campaña \"Promo Verano 2x1\" requiere atención adicional de nuestro equipo.\n\nUn miembro de FitZone Centro te contactará para resolver la situación.\n\nMientras tanto, la campaña se mantiene pausada.\n— FitZone Centro\n```\n\n### Al equipo interno (notificación de escalamiento):\n\n```\n🚨 ESCALAMIENTO — Challenge Promotion Agent\n\n📋 Caso: [escalamiento_id]\n📋 Campaña: [tema_campana] ([campaign_id])\n👤 Solicitante: [solicitante]\n📋 Tipo: [tipo_campana]\n📊 Piezas: [total_piezas]\n\n⚠️ Motivo: [motivo_escalamiento]\n📝 Detalle: [descripcion_caso]\n🔴 Urgencia: [urgencia]\n⏰ Plazo de respuesta: [plazo] horas\n👤 Asignado a: [asignado_a]\n\n📊 Estado actual:\n- Revisiones realizadas: [revisiones_realizadas]/3\n- Estado: [estado_campana]\n- Piezas conformes marca: [piezas_conforme]/[total_piezas]\n```\n\n**Ejemplo:**\n\n```\n🚨 ESCALAMIENTO — Challenge Promotion Agent\n\n📋 Caso: CPA-ESC-20260826100000\n📋 Campaña: Promo Verano 2x1 (CAMP-20260823-002)\n👤 Solicitante: Gerencia FitZone Centro\n📋 Tipo: Promo\n📊 Piezas: 8\n\n⚠️ Motivo: maximo_revisiones_alcanzado\n📝 Detalle: La campaña 'Promo Verano 2x1' lleva 3 rondas de revisión sin aprobación. El responsable solicitó cambios en cada ronda y la última solicitud requiere un cambio de tono que afecta la identidad de la marca. Se necesita intervención directa para definir la dirección final.\n🔴 Urgencia: media\n⏰ Plazo de respuesta: 24 horas\n👤 Asignado a: Gerencia FitZone Centro\n\n📊 Estado actual:\n- Revisiones realizadas: 3/3\n- Estado: con_cambios\n- Piezas conformes marca: 8/8\n```\n\n---\n\n## Ejemplo completo de escalamiento\n\n**Caso:** La campaña \"Promo Verano 2x1\" lleva 3 rondas de revisión sin aprobación.\n\n**Registro de escalamiento generado:**\n\n```json\n{\n  \"escalamiento_id\": \"CPA-ESC-20260826100000\",\n  \"agente\": \"challenge-promotion-agent\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"campaign_id\": \"CAMP-20260823-002\",\n  \"tema_campana\": \"Promo Verano 2x1\",\n  \"solicitante\": \"Gerencia FitZone Centro\",\n  \"motivo_escalamiento\": \"maximo_revisiones_alcanzado\",\n  \"descripcion_caso\": \"La campaña 'Promo Verano 2x1' lleva 3 rondas de revisión sin aprobación. El responsable solicitó cambios en cada ronda y la última solicitud requiere un cambio de tono que afecta la identidad de la marca. Se necesita intervención directa para definir la dirección final.\",\n  \"urgencia\": \"media\",\n  \"asignado_a\": \"Gerencia FitZone Centro\",\n  \"timestamp_escalamiento\": \"2026-08-26T10:00:00Z\",\n  \"datos_contexto\": {\n    \"tipo_campana\": \"promo\",\n    \"total_piezas\": 8,\n    \"revisiones_realizadas\": 3,\n    \"estado_campana\": \"con_cambios\"\n  }\n}\n```\n\n**Acciones tomadas:**\n1. Se envió mensaje al solicitante indicando que un humano lo contactará.\n2. Se notificó a Gerencia FitZone Centro por email.\n3. La campaña se pausó y no se generan nuevas versiones hasta recibir instrucción.\n4. El caso quedó marcado como `escorado` hasta recibir decisión final.\n\n---\n*Plantilla generada para el nicho Gimnasios y Fitness — Agente Challenge Promotion Agent*\n"
              },
              {
                "id": "challenge-promotion-agent/variables-entrada.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "variables-entrada.md",
                "contenido": "# Variables de entrada — Challenge Promotion Agent\n\nEste archivo define la lista fija y cerrada de campos de datos que el agente necesita recibir para operar. No se admiten campos adicionales a los listados aquí.\n\n---\n\n## Campos del evento disparador\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 1 | `campaign_id` | `string` | Opcional (auto-generado si no se proporciona) | Identificador único de la campaña. Formato: `CAMP-YYYYMMDD-XXX`. | `\"CAMP-20260823-001\"` |\n| 2 | `evento_tipo` | `string` (enum) | Obligatorio | Tipo de evento. Valores: `solicitud_campana`, `solicitud_pieza`, `solicitud_edicion`. | `\"solicitud_campana\"` |\n| 3 | `fecha_hora_solicitud` | `string` (ISO 8601) | Obligatorio | Fecha y hora de la solicitud. | `\"2026-08-23T10:00:00Z\"` |\n| 4 | `solicitante` | `string` | Obligatorio | Nombre de la persona que solicita la campaña. | `\"Gerencia FitZone Centro\"` |\n| 5 | `email_solicitante` | `string` | Obligatorio | Correo electrónico del solicitante. | `\"gerencia@fitzone.com\"` |\n\n---\n\n## Datos de la campaña\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 6 | `objetivo_campana` | `string` (enum) | Obligatorio | Objetivo de la campaña. Valores: `incrementar_asistencia`, `generar_inscripciones`, `retener_miembros`, `reactivar_inactivos`, `promocionar_servicio`, `generar_engagement`, `construir_comunidad`. | `\"incrementar_asistencia\"` |\n| 7 | `tema_campana` | `string` | Obligatorio | Nombre o tema de la campaña. Máximo 100 caracteres. | `\"Reto 30 Días Spinning\"` |\n| 8 | `descripcion_breve` | `string` | Obligatorio | Descripción breve de la campaña. Máximo 500 caracteres. | `\"Campaña para aumentar la asistencia a clases de Spinning con un reto de 30 días.\"` |\n| 9 | `tipo_campana` | `string` (enum) | Obligatorio | Tipo de campaña. Valores: `reto`, `promo`, `evento`, `testimonio`, `contenido`, `referido`, `reactivacion`, `estacional`. | `\"reto\"` |\n| 10 | `canales_objetivo` | `array` de strings | Obligatorio | Canales donde se publicará la campaña. Valores: `instagram`, `facebook`, `whatsapp_broadcast`, `email_newsletter`, `tiktok`, `google_ads`, `sms`. | `[\"instagram\", \"whatsapp_broadcast\", \"email_newsletter\"]` |\n| 11 | `fecha_inicio_deseada` | `string` (YYYY-MM-DD) | Obligatorio | Fecha de inicio deseada para la campaña. | `\"2026-09-01\"` |\n| 12 | `fecha_fin_deseada` | `string` (YYYY-MM-DD) | Obligatorio | Fecha de fin deseada para la campaña. | `\"2026-09-30\"` |\n| 13 | `presupuesto_estimado` | `number` | Opcional | Presupuesto estimado para la campaña (publicidad pagada). | `50000` |\n| 14 | `moneda` | `string` (ISO 4217) | Opcional (default: `\"ARS\"`) | Código de moneda. | `\"ARS\"` |\n\n---\n\n## Datos del público objetivo\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 15 | `publico_objetivo` | `string` | Obligatorio | Descripción del público objetivo de la campaña. | `\"Miembros actuales y personas interesadas en spinning dentro del radio de 5km\"` |\n| 16 | `publico_segmentos` | `array` de strings | Opcional | Segmentos específicos a targetear. | `[\"miembros_activos\", \"leads_warm\", \"inactivos_30d\"]` |\n| 17 | `publico_exclusiones` | `array` de strings | Opcional | Segmentos a excluir. | `[\"miembros_suspendidos\", \"cancelados_ultimo_mes\"]` |\n\n---\n\n## Datos de tono y estilo\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 18 | `tono_deseado` | `string` | Opcional | Tono deseado para el contenido. | `\"Enérgico, motivador, competitivo\"` |\n| 19 | `referencias` | `array` de strings | Opcional | Referencias de campañas o contenido similar. | `[\"Campaña similar de SoulCycle\", \"Reto 21 días de Peloton\"]` |\n| 20 | `restricciones` | `array` de strings | Opcional | Restricciones o limitaciones para el contenido. | `[\"No mencionar precios menores a $8.000\", \"No usar fotos de modelos stock\"]` |\n\n---\n\n## Guía de marca del gimnasio\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 21 | `guia_marca` | `object` | Obligatorio | Guía de marca completa del gimnasio. | Ver estructura abajo |\n\n**Estructura de `guia_marca`:**\n\n| Campo | Tipo | Descripción | Ejemplo |\n|---|---|---|---|\n| `colores_principales` | `array` de strings | Colores hexadecimales de la marca | `[\"#FF4500\", \"#1A1A2E\", \"#FFFFFF\"]` |\n| `tipografia_titulos` | `string` | Tipografía para títulos | `\"Montserrat Bold\"` |\n| `tipografia_cuerpo` | `string` | Tipografía para cuerpo de texto | `\"Open Sans\"` |\n| `tono_marca` | `string` | Tono general de la marca | `\"Enérgico, cercano, sin ser informal\"` |\n| `emojis_permitidos` | `boolean` | Si se permiten emojis en el contenido | `true` |\n| `hashtags_fijos` | `array` de strings | Hashtags que deben aparecer en todas las piezas | `[\"#FitZone\", \"#FitZoneCentro\", \"#TuGimnasio\"]` |\n| `logo_url` | `string` o `null` | URL del logo para incluir en las piezas | `\"https://fitzone.com/logo.png\"` |\n| `estilo_fotografia` | `string` | Estilo de fotografía permitido | `\"Personas reales entrenando, sin modelos stock\"` |\n\n---\n\n## Datos del gimnasio\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 22 | `gimnasio_id` | `string` | Obligatorio | Identificador único del gimnasio. | `\"GYM-001\"` |\n| 23 | `gimnasio_nombre` | `string` | Obligatorio | Nombre comercial del gimnasio. | `\"FitZone Centro\"` |\n| 24 | `gimnasio_telefono` | `string` | Opcional | Teléfono del gimnasio. | `\"+54 11 4444-5678\"` |\n| 25 | `gimnasio_direccion` | `string` | Opcional | Dirección del gimnasio. | `\"Av. Principal 1234, CABA\"` |\n| 26 | `gimnasio_sitio_web` | `string` | Opcional | URL del sitio web. | `\"https://fitzone.com\"` |\n\n---\n\n## Datos de aprobación\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 27 | `responsable_aprobacion` | `string` | Obligatorio | Nombre del responsable que debe aprobar la campaña. | `\"Carlos Pérez (Gerente de Marketing)\"` |\n| 28 | `email_responsable` | `string` | Obligatorio | Correo electrónico del responsable. | `\"carlos.perez.marketing@fitzone.com\"` |\n| 29 | `canal_notificacion` | `string` (enum) | Obligatorio | Canal para notificar al responsable. Valores: `email`, `whatsapp`. | `\"email\"` |\n| 30 | `telefono_responsable` | `string` | Condicional (requerido si `canal_notificacion` es `whatsapp`) | Teléfono del responsable. | `\"+54 11 4444-5678\"` |\n\n---\n\n## Datos de tiempos\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 31 | `tiempo_fecha_actual` | `string` (ISO 8601) | Obligatorio | Fecha y hora actual del sistema. | `\"2026-08-23T10:00:00Z\"` |\n| 32 | `plazo_aprobacion_horas` | `integer` | Opcional (default: 48) | Horas de plazo para la aprobación. | `48` |\n| 33 | `max_revisiones` | `integer` | Opcional (default: 3) | Máximo de rondas de revisión antes de escalar. | `3` |\n\n---\n\n## Campos calculados por el agente\n\nEstos campos **no se reciben de entrada**; el agente los genera internamente.\n\n| # | Nombre del campo | Tipo de dato | Generado en | Descripción |\n|---|---|---|---|---|\n| 34 | `fecha_hora_recepcion` | `string` (ISO 8601) | Paso 1 | Marca de tiempo de recepción. |\n| 35 | `total_piezas` | `integer` | Paso 2 | Total de piezas generadas. |\n| 36 | `total_canales` | `integer` | Paso 2 | Total de canales cubiertos. |\n| 37 | `calendario` | `array` de objetos | Paso 2 | Calendario de publicación. |\n| 38 | `piezas` | `array` de objetos | Paso 2 | Detalle de cada pieza. |\n| 39 | `informe_marca` | `object` | Paso 3 | Informe de consistencia de marca. |\n| 40 | `fecha_hora_envio_propuesta` | `string` (ISO 8601) | Paso 4 | Marca de envío de la propuesta. |\n| 41 | `revisiones_realizadas` | `integer` | Paso 5 | Número de rondas de revisión. |\n| 42 | `respuesta_aprobacion` | `string` (enum) o `null` | Paso 5 | Respuesta del responsable. |\n| 43 | `texto_respuesta` | `string` o `null` | Paso 5 | Texto de la respuesta. |\n| 44 | `fecha_hora_respuesta` | `string` (ISO 8601) o `null` | Paso 5 | Marca de la respuesta. |\n| 45 | `estado_campana` | `string` (enum) | Paso 5 | Estado actual de la campaña. |\n| 46 | `fecha_hora_cierre` | `string` (ISO 8601) | Paso 5 | Marca de cierre. |\n| 47 | `duracion_proceso_horas` | `number` | Paso 5 | Duración total del proceso. |\n\n---\n\n## Validaciones de entrada\n\n| Campo | Regla | Acción si falla |\n|---|---|---|\n| `evento_tipo` | Debe ser uno de los 3 valores válidos | Terminar flujo sin ejecutar |\n| `objetivo_campana` | Debe ser uno de los 7 valores válidos | Terminar flujo sin ejecutar |\n| `tema_campana` | No vacío, máximo 100 caracteres | Terminar flujo sin ejecutar |\n| `descripcion_breve` | No vacío, máximo 500 caracteres | Usar tema como descripción |\n| `tipo_campana` | Debe ser uno de los 8 valores válidos | Terminar flujo sin ejecutar |\n| `canales_objetivo` | Array con al menos 1 canal válido | Terminar flujo sin ejecutar |\n| `fecha_inicio_deseada` | Formato YYYY-MM-DD, no anterior a `tiempo_fecha_actual` | Usar `tiempo_fecha_actual` + 7 días |\n| `fecha_fin_deseada` | Formato YYYY-MM-DD, posterior a `fecha_inicio_deseada` | Usar `fecha_inicio_deseada` + 30 días |\n| `publico_objetivo` | No vacío | Terminar flujo sin ejecutar |\n| `guia_marca` | Objeto con campos requeridos | Usar guía de marca default del nicho |\n| `responsable_aprobacion` | No vacío | Terminar flujo sin ejecutar |\n| `email_responsable` | Formato de email válido | Escalar a humano (C4.1) |\n\n---\n\n## Ejemplo de payload completo\n\n```json\n{\n  \"campaign_id\": \"CAMP-20260823-001\",\n  \"evento_tipo\": \"solicitud_campana\",\n  \"fecha_hora_solicitud\": \"2026-08-23T10:00:00Z\",\n  \"solicitante\": \"Gerencia FitZone Centro\",\n  \"email_solicitante\": \"gerencia@fitzone.com\",\n  \"objetivo_campana\": \"incrementar_asistencia\",\n  \"tema_campana\": \"Reto 30 Días Spinning\",\n  \"descripcion_breve\": \"Campaña para aumentar la asistencia a clases de Spinning con un reto de 30 días. Incluir inscripción gratuita del reto y descuento en membresías nuevas.\",\n  \"tipo_campana\": \"reto\",\n  \"canales_objetivo\": [\"instagram\", \"whatsapp_broadcast\", \"email_newsletter\"],\n  \"fecha_inicio_deseada\": \"2026-09-01\",\n  \"fecha_fin_deseada\": \"2026-09-30\",\n  \"presupuesto_estimado\": 50000,\n  \"moneda\": \"ARS\",\n  \"publico_objetivo\": \"Miembros actuales y personas interesadas en spinning dentro del radio de 5km\",\n  \"publico_segmentos\": [\"miembros_activos\", \"leads_warm\", \"inactivos_30d\"],\n  \"publico_exclusiones\": [\"miembros_suspendidos\", \"cancelados_ultimo_mes\"],\n  \"tono_deseado\": \"Enérgico, motivador, competitivo\",\n  \"referencias\": [\"Campaña similar de SoulCycle\", \"Reto 21 días de Peloton\"],\n  \"restricciones\": [\"No mencionar precios menores a $8.000\", \"No usar fotos de modelos stock\", \"Incluir logo en todas las piezas\"],\n  \"guia_marca\": {\n    \"colores_principales\": [\"#FF4500\", \"#1A1A2E\", \"#FFFFFF\"],\n    \"tipografia_titulos\": \"Montserrat Bold\",\n    \"tipografia_cuerpo\": \"Open Sans\",\n    \"tono_marca\": \"Enérgico, cercano, sin ser informal\",\n    \"emojis_permitidos\": true,\n    \"hashtags_fijos\": [\"#FitZone\", \"#FitZoneCentro\", \"#TuGimnasio\"],\n    \"logo_url\": \"https://fitzone.com/logo.png\",\n    \"estilo_fotografia\": \"Personas reales entrenando, sin modelos stock\"\n  },\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"gimnasio_telefono\": \"+54 11 4444-5678\",\n  \"gimnasio_direccion\": \"Av. Principal 1234, CABA\",\n  \"gimnasio_sitio_web\": \"https://fitzone.com\",\n  \"responsable_aprobacion\": \"Carlos Pérez (Gerente de Marketing)\",\n  \"email_responsable\": \"carlos.perez.marketing@fitzone.com\",\n  \"canal_notificacion\": \"email\",\n  \"telefono_responsable\": null,\n  \"tiempo_fecha_actual\": \"2026-08-23T10:00:00Z\",\n  \"plazo_aprobacion_horas\": 48,\n  \"max_revisiones\": 3\n}\n```\n\n---\n*Plantilla generada para el nicho Gimnasios y Fitness — Agente Challenge Promotion Agent*\n"
              }
            ]
          }
        ]
      },
      {
        "id": "referral-request",
        "carpeta": "referral-request-agent",
        "nombre": "Referral Request Agent",
        "icono": "🔗",
        "descripcion": "Solicitar referidos a miembros satisfechos con incentivos",
        "submodulos": [],
        "categoria": "general",
        "estructura": [
          {
            "id": "referral-request-agent",
            "etiqueta": "",
            "es_plantilla": true,
            "nombre": "referral-request-agent",
            "subcarpetas": [],
            "notas": [
              {
                "id": "referral-request-agent/flujo.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "flujo.md",
                "contenido": "# Flujo de ejecución — Referral Request Agent\n\nEste archivo documenta el flujo fijo de ejecución del agente. Cada solicitud de referido sigue estos 5 pasos **en orden**, sin pasos condicionales ni bifurcaciones.\n\n---\n\n## Paso 1 — Detección de un evento de satisfacción confirmado\n\nEl agente detecta un evento que indica satisfacción del miembro y activa el flujo de solicitud de referidos.\n\n**Acción del agente:**\n1. Recibir los datos del evento según lo definido en `variables-entrada.md`.\n2. Registrar la marca de tiempo de detección en `fecha_hora_deteccion_evento`.\n3. Validar que el `contacto_id` tenga un formato válido.\n4. Verificar que el `evento_tipo` sea uno de los eventos válidos de satisfacción.\n5. Verificar que el miembro tenga al menos 14 días de membresía activa.\n6. Verificar que no se haya enviado una solicitud de referido en los últimos 30 días.\n7. Verificar que el `programa_referidos` esté activo.\n8. Determinar el canal de comunicación preferido del miembro.\n9. Generar el `referal_request_id` único.\n\n**Tabla de verificación previa al envío:**\n\n| Verificación | Regla | Acción si falla |\n|---|---|---|\n| Membresía >= 14 días | `fecha_inicio_membresia` <= `tiempo_fecha_actual` - 14 días | No enviar, registrar como `miembro_nuevo` |\n| Sin solicitud en 30 días | No hay `solicitud_referido` en últimos 30 días | No enviar, registrar como `ya_solicitado` |\n| Programa activo | `programa_referidos_estado` = `activo` | No enviar, registrar como `programa_inactivo` |\n| Canal válido | `canal_preferido` en lista de canales soportados | Usar `email` como respaldo |\n\n**Ejemplo de evento detectado (reseña positiva):**\n\n```json\n{\n  \"evento_id\": \"REF-EVT-20260823-143000-001\",\n  \"contacto_id\": \"CT-2026-00452\",\n  \"evento_tipo\": \"resena_positiva\",\n  \"evento_detalle\": \"Reseña de 5 estrellas en Google\",\n  \"fecha_hora_evento\": \"2026-08-23T12:00:00Z\",\n  \"nombre_miembro\": \"María López\",\n  \"telefono_miembro\": \"+54 11 5555-1234\",\n  \"email_miembro\": \"maria.lopez@email.com\",\n  \"canal_preferido\": \"whatsapp\",\n  \"membresia_tipo\": \"premium\",\n  \"membresia_estado\": \"activa\",\n  \"membresia_fecha_inicio\": \"2026-07-01\",\n  \"dias_membresia\": 53,\n  \"programa_referidos\": {\n    \"codigo\": \"descuento_membresia\",\n    \"nombre\": \"Descuento en membresía\",\n    \"incentivo_referidor\": \"10% de descuento en la próxima mensualidad\",\n    \"incentivo_referido\": \"15% de descuento en la primera mensualidad\",\n    \"validez_dias\": 30,\n    \"estado\": \"activo\"\n  },\n  \"codigo_referido_asignado\": \"MARIA-LOPEZ-2026\",\n  \"enlace_referido\": \"https://app.fitzone.com/r/MARIA-LOPEZ-2026\",\n  \"solicitudes_referido_previas\": 0,\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"gimnasio_telefono\": \"+54 11 4444-5678\",\n  \"responsable_programa\": \"Departamento de Marketing\",\n  \"email_responsable\": \"marketing@fitzone.com\",\n  \"canal_notificacion_equipo\": \"email\",\n  \"horario_atencion_inicio\": \"07:00\",\n  \"horario_atencion_fin\": \"22:00\",\n  \"tiempo_fecha_actual\": \"2026-08-23T14:30:00Z\"\n}\n```\n\n**Ejemplo de evento detectado (10ª asistencia al mes):**\n\n```json\n{\n  \"evento_id\": \"REF-EVT-20260823-143000-002\",\n  \"contacto_id\": \"CT-2026-00389\",\n  \"evento_tipo\": \"decima_asistencia_mes\",\n  \"evento_detalle\": \"10 visitas registradas en agosto 2026\",\n  \"fecha_hora_evento\": \"2026-08-23T09:30:00Z\",\n  \"nombre_miembro\": \"Roberto García\",\n  \"telefono_miembro\": \"+54 11 6666-7890\",\n  \"email_miembro\": \"roberto.garcia@email.com\",\n  \"canal_preferido\": \"whatsapp\",\n  \"membresia_tipo\": \"basico\",\n  \"membresia_estado\": \"activa\",\n  \"membresia_fecha_inicio\": \"2026-06-15\",\n  \"dias_membresia\": 69,\n  \"programa_referidos\": {\n    \"codigo\": \"clase_gratis\",\n    \"nombre\": \"Clase gratis\",\n    \"incentivo_referidor\": \"1 clase gratis\",\n    \"incentivo_referido\": \"1 clase gratis\",\n    \"validez_dias\": 30,\n    \"estado\": \"activo\"\n  },\n  \"codigo_referido_asignado\": \"ROBERTO-GARCIA-2026\",\n  \"enlace_referido\": \"https://app.fitzone.com/r/ROBERTO-GARCIA-2026\",\n  \"solicitudes_referido_previas\": 1,\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"gimnasio_telefono\": \"+54 11 4444-5678\",\n  \"responsable_programa\": \"Departamento de Marketing\",\n  \"email_responsable\": \"marketing@fitzone.com\",\n  \"canal_notificacion_equipo\": \"email\",\n  \"horario_atencion_inicio\": \"07:00\",\n  \"horario_atencion_fin\": \"22:00\",\n  \"tiempo_fecha_actual\": \"2026-08-23T14:30:00Z\"\n}\n```\n\n---\n\n## Paso 2 — Envío del mensaje solicitando referidos con el incentivo definido\n\nEl agente envía el mensaje de solicitud de referido al miembro, incluyendo el incentivo y el enlace/código.\n\n**Acción del agente:**\n1. Generar el mensaje de solicitud usando la plantilla de `formato-salida.md` → sección 1.\n2. Insertar el código de referido, enlace e incentivos.\n3. Enviar el mensaje por el `canal_preferido`.\n4. Si el envío falla, reintentar una sola vez después de 30 segundos.\n5. Si el segundo intento falla, registrar el error pero continuar (el caso se cierra como `envio_fallido`).\n6. Registrar la marca de tiempo de envío en `fecha_hora_envio_solicitud`.\n7. Actualizar el estado del caso a `solicitud_enviada`.\n8. Incrementar el contador `solicitudes_referido_previas`.\n\n**Plantilla de solicitud de referido (WhatsApp/SMS):**\n\n```\n¡Hola [nombre_miembro]! Nos alegra que estés disfrutando de [gimnasio_nombre] 💪\n\n¿Conocés a alguien que le gustaría entrenar con nosotros? Si le compartís tu código, ambos reciben un beneficio:\n\n🎁 Para vos: [incentivo_referidor]\n🎁 Para tu amigo/a: [incentivo_referido]\n\nTu código: [codigo_referido]\n👉 [enlace_referido]\n\nCompartilo con quien quieras. ¡Gracias por ser parte de la comunidad!\n— [gimnasio_nombre]\n```\n\n**Plantilla de solicitud de referido (email):**\n\n**Asunto:** ¡Compartí [gimnasio_nombre] y ambos ganan! 🎁\n\n```\n¡Hola [nombre_miembro]!\n\nNos alegra que estés disfrutando de [gimnasio_nombre]. Tu experiencia nos motiva a seguir mejorando cada día 💪\n\n¿Conocés a alguien que le gustaría entrenar con nosotros? Queremos premiar tu confianza.\n\n🎁 Tu beneficio por referir: [incentivo_referidor]\n🎁 Beneficio de tu amigo/a: [incentivo_referido]\n\nTu código de referido:\n👉 [codigo_referido]\n\nO compartí este enlace directamente:\n👉 [enlace_referido]\n\nEl beneficio es válido por [validez_dias] días.\n\n¡Gracias por ser parte de la comunidad [gimnasio_nombre]!\n— El equipo de [gimnasio_nombre]\n```\n\n**Ejemplo renderizado de solicitud (WhatsApp):**\n\n```\n¡Hola María López! Nos alegra que estés disfrutando de FitZone Centro 💪\n\n¿Conocés a alguien que le gustaría entrenar con nosotros? Si le compartís tu código, ambos reciben un beneficio:\n\n🎁 Para vos: 10% de descuento en la próxima mensualidad\n🎁 Para tu amigo/a: 15% de descuento en la primera mensualidad\n\nTu código: MARIA-LOPEZ-2026\n👉 https://app.fitzone.com/r/MARIA-LOPEZ-2026\n\nCompartilo con quien quieras. ¡Gracias por ser parte de la comunidad!\n— FitZone Centro\n```\n\n**Ejemplo renderizado de solicitud (email):**\n\n```\nAsunto: ¡Compartí FitZone Centro y ambos ganan! 🎁\n\n¡Hola María López!\n\nNos alegra que estés disfrutando de FitZone Centro. Tu experiencia nos motiva a seguir mejorando cada día 💪\n\n¿Conocés a alguien que le gustaría entrenar con nosotros? Queremos premiar tu confianza.\n\n🎁 Tu beneficio por referir: 10% de descuento en la próxima mensualidad\n🎁 Beneficio de tu amigo/a: 15% de descuento en la primera mensualidad\n\nTu código de referido:\n👉 MARIA-LOPEZ-2026\n\nO compartí este enlace directamente:\n👉 https://app.fitzone.com/r/MARIA-LOPEZ-2026\n\nEl beneficio es válido por 30 días.\n\n¡Gracias por ser parte de la comunidad FitZone Centro!\n— El equipo de FitZone Centro\n```\n\n---\n\n## Paso 3 — Generación del enlace/código de referido único del contacto\n\nEl agente verifica que el código y enlace de referido estén correctamente generados y activos.\n\n**Acción del agente:**\n1. Verificar que el `codigo_referido_asignado` sea único y no esté en uso por otro miembro.\n2. Verificar que el `enlace_referido` sea accesible y devuelva una página válida.\n3. Si el código ya existe para otro miembro, generar uno nuevo con formato `[NOMBRE]-[AÑO]-[SECUENCIA]`.\n4. Registrar el código en la base de datos de referidos.\n5. Verificar que el programa de referidos asociado al código esté activo.\n6. Registrar la marca de tiempo de generación en `fecha_hora_generacion_codigo`.\n\n**Tabla de formatos de código de referido:**\n\n| Formato | Ejemplo | Usado cuando |\n|---|---|---|\n| `[NOMBRE]-[AÑO]` | `MARIA-LOPEZ-2026` | Código base |\n| `[NOMBRE]-[AÑO]-[SECUENCIA]` | `MARIA-LOPEZ-2026-2` | Si el base ya existe |\n| `[ABREV]-[YYYYMMDD]` | `ML-20260823` | Si el nombre es muy largo |\n\n**Estructura del registro de código:**\n\n```json\n{\n  \"codigo_referido\": \"MARIA-LOPEZ-2026\",\n  \"contacto_id\": \"CT-2026-00452\",\n  \"nombre_miembro\": \"María López\",\n  \"programa_referidos\": \"descuento_membresia\",\n  \"enlace_referido\": \"https://app.fitzone.com/r/MARIA-LOPEZ-2026\",\n  \"fecha_generacion\": \"2026-08-23T14:30:00Z\",\n  \"fecha_expiracion\": \"2026-09-22T14:30:00Z\",\n  \"estado\": \"activo\",\n  \"usos_totales\": 0,\n  \"referidos_convertidos\": 0\n}\n```\n\n---\n\n## Paso 4 — Registro cuando el enlace/código sea utilizado por un nuevo contacto\n\nEl agente detecta y registra cuando un nuevo contacto usa el código de referido.\n\n**Acción del agente:**\n1. Detectar que el código de referido fue utilizado por un nuevo contacto.\n2. Validar que el `evento_tipo` sea `codigo_utilizado`.\n3. Registrar los datos del nuevo contacto (referido).\n4. Verificar que el referido no sea un miembro existente.\n5. Verificar que el código no haya expirado.\n6. Actualizar el contador `usos_totales` del código.\n7. Registrar la marca de tiempo de uso en `fecha_hora_uso_codigo`.\n8. Proceder al paso 5 si el referido se concretó (se inscribió).\n\n**Tabla de estados de un referido:**\n\n| Estado | Condición |\n|---|---|\n| `codigo_generado` | Código creado, esperando uso |\n| `codigo_utilizado` | Código usado por un nuevo contacto |\n| `referido_inscrito` | El referido completó la inscripción |\n| `referido_no_inscrito` | El referido usó el código pero no se inscribió |\n| `codigo_expirado` | El código venció sin uso |\n| `codigo_cancelado` | El código fue cancelado manualmente |\n\n**Ejemplo de registro de código utilizado:**\n\n```json\n{\n  \"evento_id\": \"REF-USE-20260825-100000-001\",\n  \"codigo_referido\": \"MARIA-LOPEZ-2026\",\n  \"contacto_referidor_id\": \"CT-2026-00452\",\n  \"nombre_referidor\": \"María López\",\n  \"contacto_referido_id\": \"CT-2026-00801\",\n  \"nombre_referido\": \"Ana García\",\n  \"telefono_referido\": \"+54 11 8888-1234\",\n  \"email_referido\": \"ana.garcia@email.com\",\n  \"fecha_hora_uso_codigo\": \"2026-08-25T10:00:00Z\",\n  \"evento_tipo\": \"codigo_utilizado\",\n  \"programa_referidos\": \"descuento_membresia\",\n  \"incentivo_referidor\": \"10% de descuento en la próxima mensualidad\",\n  \"incentivo_referido\": \"15% de descuento en la primera mensualidad\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"tiempo_fecha_actual\": \"2026-08-25T10:00:00Z\"\n}\n```\n\n---\n\n## Paso 5 — Notificación al equipo humano cuando se concrete un referido\n\nEl agente notifica al equipo humano cuando un referido se concretó (el referido completó la inscripción).\n\n**Acción del agente:**\n1. Verificar que el referido completó la inscripción (`referido_inscrito`).\n2. Calcular el total de referidos convertidos del código.\n3. Generar la notificación al equipo usando la plantilla de `formato-salida.md` → sección 3.\n4. Enviar la notificación al `responsable_programa` por el `canal_notificacion_equipo`.\n5. Enviar mensaje de agradecimiento al referidor (ver `formato-salida.md` → sección 4).\n6. Registrar la marca de tiempo de notificación en `fecha_hora_notificacion`.\n7. Actualizar el estado del referido a `referido_inscrito`.\n8. Generar el registro de resultado final.\n\n**Plantilla de notificación al equipo (email):**\n\n**Asunto:** ¡Referido concretado! — [nombre_referidor] → [nombre_referido] | [gimnasio_nombre]\n\n```\nHola [responsable_nombre],\n\nUn referido se concretó exitosamente.\n\n👤 Referidor: [nombre_referidor] ([contacto_referidor_id])\n🔗 Código: [codigo_referido]\n📅 Código generado: [fecha_generacion]\n\n👤 Referido: [nombre_referido] ([contacto_referido_id])\n📅 Inscripción: [fecha_inscripcion]\n📋 Tipo de membresía: [membresia_tipo_referido]\n\n🎁 Beneficios a aplicar:\n- Referidor: [incentivo_referidor]\n- Referido: [incentivo_referido]\n\n📊 Total de referidos de este código: [total_referidos]\n— [gimnasio_nombre]\n```\n\n**Plantilla de agradecimiento al referidor (WhatsApp/SMS):**\n\n```\n¡[nombre_referidor], gracias por referirnos! 🎉\n\nTu amigo/a [nombre_referido] ya se unió a [gimnasio_nombre].\n\n🎁 Tu beneficio: [incentivo_referidor]\nLo encontrarás aplicado en tu próxima factura o disponoble para reclamar en recepción.\n\n¡Gracias por ser parte de la comunidad! 💪\n— [gimnasio_nombre]\n```\n\n**Ejemplo renderizado de notificación al equipo (email):**\n\n```\nAsunto: ¡Referido concretado! — María López → Ana García | FitZone Centro\n\nHola Departamento de Marketing,\n\nUn referido se concretó exitosamente.\n\n👤 Referidor: María López (CT-2026-00452)\n🔗 Código: MARIA-LOPEZ-2026\n📅 Código generado: 23/08/2026\n\n👤 Referido: Ana García (CT-2026-00801)\n📅 Inscripción: 25/08/2026\n📋 Tipo de membresía: Premium\n\n🎁 Beneficios a aplicar:\n- Referidor: 10% de descuento en la próxima mensualidad\n- Referido: 15% de descuento en la primera mensualidad\n\n📊 Total de referidos de este código: 1\n— FitZone Centro\n```\n\n**Ejemplo renderizado de agradecimiento al referidor (WhatsApp):**\n\n```\n¡María López, gracias por referirnos! 🎉\n\nTu amigo/a Ana García ya se unió a FitZone Centro.\n\n🎁 Tu beneficio: 10% de descuento en la próxima mensualidad\nLo encontrarás aplicado en tu próxima factura o disponoble para reclamar en recepción.\n\n¡Gracias por ser parte de la comunidad! 💪\n— FitZone Centro\n```\n\n---\n*Plantilla generada para el nicho Gimnasios y Fitness — Agente Referral Request Agent*\n"
              },
              {
                "id": "referral-request-agent/formato-salida.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "formato-salida.md",
                "contenido": "# Formato de salida — Referral Request Agent\n\n## Descripción\n\nDefine la estructura exacta del mensaje o registro que el agente debe producir en cada paso del flujo. Todos los campos deben tener valores concretos de ejemplo.\n\n---\n\n## 1. Mensaje de solicitud de referido\n\n**Cuándo se genera:** Paso 2 — Cuando se envía la solicitud de referido al miembro.\n\n**Plantilla (WhatsApp/SMS):**\n\n```\n¡Hola [nombre_miembro]! Nos alegra que estés disfrutando de [gimnasio_nombre] 💪\n\n¿Conocés a alguien que le gustaría entrenar con nosotros? Si le compartís tu código, ambos reciben un beneficio:\n\n🎁 Para vos: [incentivo_referidor]\n🎁 Para tu amigo/a: [incentivo_referido]\n\nTu código: [codigo_referido]\n👉 [enlace_referido]\n\nCompartilo con quien quieras. ¡Gracias por ser parte de la comunidad!\n— [gimnasio_nombre]\n```\n\n**Plantilla (email):**\n\n**Asunto:** ¡Compartí [gimnasio_nombre] y ambos ganan! 🎁\n\n```\n¡Hola [nombre_miembro]!\n\nNos alegra que estés disfrutando de [gimnasio_nombre]. Tu experiencia nos motiva a seguir mejorando cada día 💪\n\n¿Conocés a alguien que le gustaría entrenar con nosotros? Queremos premiar tu confianza.\n\n🎁 Tu beneficio por referir: [incentivo_referidor]\n🎁 Beneficio de tu amigo/a: [incentivo_referido]\n\nTu código de referido:\n👉 [codigo_referido]\n\nO compartí este enlace directamente:\n👉 [enlace_referido]\n\nEl beneficio es válido por [validez_dias] días.\n\n¡Gracias por ser parte de la comunidad [gimnasio_nombre]!\n— El equipo de [gimnasio_nombre]\n```\n\n**Ejemplo renderizado (WhatsApp — programa descuento):**\n\n```\n¡Hola María López! Nos alegra que estés disfrutando de FitZone Centro 💪\n\n¿Conocés a alguien que le gustaría entrenar con nosotros? Si le compartís tu código, ambos reciben un beneficio:\n\n🎁 Para vos: 10% de descuento en la próxima mensualidad\n🎁 Para tu amigo/a: 15% de descuento en la primera mensualidad\n\nTu código: MARIA-LOPEZ-2026\n👉 https://app.fitzone.com/r/MARIA-LOPEZ-2026\n\nCompartilo con quien quieras. ¡Gracias por ser parte de la comunidad!\n— FitZone Centro\n```\n\n**Ejemplo renderizado (email — programa clase gratis):**\n\n```\nAsunto: ¡Compartí FitZone Centro y ambos ganan! 🎁\n\n¡Hola Roberto García!\n\nNos alegra que estés disfrutando de FitZone Centro. Tu experiencia nos motiva a seguir mejorando cada día 💪\n\n¿Conocés a alguien que le gustaría entrenar con nosotros? Queremos premiar tu confianza.\n\n🎁 Tu beneficio por referir: 1 clase gratis\n🎁 Beneficio de tu amigo/a: 1 clase gratis\n\nTu código de referido:\n👉 ROBERTO-GARCIA-2026\n\nO compartí este enlace directamente:\n👉 https://app.fitzone.com/r/ROBERTO-GARCIA-2026\n\nEl beneficio es válido por 30 días.\n\n¡Gracias por ser parte de la comunidad FitZone Centro!\n— El equipo de FitZone Centro\n```\n\n---\n\n## 2. Confirmación de envío de solicitud\n\n**Cuándo se genera:** Paso 2 — Después de enviar la solicitud exitosamente.\n\n**Plantilla (solo registro interno, no se envía al miembro):**\n\n```\n✅ Solicitud de referido enviada\n\n👤 Miembro: [nombre_miembro]\n🔗 Código: [codigo_referido]\n📅 Envío: [fecha_hora_envio_solicitud]\n📋 Programa: [programa_nombre]\n🎁 Incentivo referidor: [incentivo_referidor]\n🎁 Incentivo referido: [incentivo_referido]\n```\n\n---\n\n## 3. Notificación al equipo — Referido concretado\n\n**Cuándo se genera:** Paso 5 — Cuando un referido completó la inscripción.\n\n**Plantilla (email):**\n\n**Asunto:** ¡Referido concretado! — [nombre_referidor] → [nombre_referido] | [gimnasio_nombre]\n\n```\nHola [responsable_nombre],\n\nUn referido se concretó exitosamente.\n\n👤 Referidor: [nombre_referidor] ([contacto_referidor_id])\n🔗 Código: [codigo_referido]\n📅 Código generado: [fecha_generacion]\n\n👤 Referido: [nombre_referido] ([contacto_referido_id])\n📅 Inscripción: [fecha_inscripcion]\n📋 Tipo de membresía: [membresia_tipo_referido]\n\n🎁 Beneficios a aplicar:\n- Referidor: [incentivo_referidor]\n- Referido: [incentivo_referido]\n\n📊 Total de referidos de este código: [total_referidos]\n— [gimnasio_nombre]\n```\n\n**Ejemplo renderizado:**\n\n```\nAsunto: ¡Referido concretado! — María López → Ana García | FitZone Centro\n\nHola Departamento de Marketing,\n\nUn referido se concretó exitosamente.\n\n👤 Referidor: María López (CT-2026-00452)\n🔗 Código: MARIA-LOPEZ-2026\n📅 Código generado: 23/08/2026\n\n👤 Referido: Ana García (CT-2026-00801)\n📅 Inscripción: 25/08/2026\n📋 Tipo de membresía: Premium\n\n🎁 Beneficios a aplicar:\n- Referidor: 10% de descuento en la próxima mensualidad\n- Referido: 15% de descuento en la primera mensualidad\n\n📊 Total de referidos de este código: 1\n— FitZone Centro\n```\n\n---\n\n## 4. Mensaje de agradecimiento al referidor\n\n**Cuándo se genera:** Paso 5 — Cuando se notifica al equipo del referido concretado.\n\n**Plantilla (WhatsApp/SMS):**\n\n```\n¡[nombre_referidor], gracias por referirnos! 🎉\n\nTu amigo/a [nombre_referido] ya se unió a [gimnasio_nombre].\n\n🎁 Tu beneficio: [incentivo_referidor]\nLo encontrarás aplicado en tu próxima factura o disponible para reclamar en recepción.\n\n¡Gracias por ser parte de la comunidad! 💪\n— [gimnasio_nombre]\n```\n\n**Ejemplo renderizado:**\n\n```\n¡María López, gracias por referirnos! 🎉\n\nTu amigo/a Ana García ya se unió a FitZone Centro.\n\n🎁 Tu beneficio: 10% de descuento en la próxima mensualidad\nLo encontrarás aplicado en tu próxima factura o disponible para reclamar en recepción.\n\n¡Gracias por ser parte de la comunidad! 💪\n— FitZone Centro\n```\n\n---\n\n## 5. Registro de acción (JSON)\n\n**Cuándo se genera:** En cada paso del flujo, como registro interno.\n\n**Estructura:**\n\n```json\n{\n  \"registro_id\": \"REF-REG-[timestamp]\",\n  \"agente\": \"referral-request-agent\",\n  \"referal_request_id\": \"[referal_request_id]\",\n  \"contacto_id\": \"[contacto_id]\",\n  \"nombre_miembro\": \"[nombre_miembro]\",\n  \"gimnasio_id\": \"[gimnasio_id]\",\n  \"gimnasio_nombre\": \"[gimnasio_nombre]\",\n  \"evento_tipo\": \"[evento_tipo]\",\n  \"programa_codigo\": \"[programa_codigo]\",\n  \"programa_nombre\": \"[programa_nombre]\",\n  \"codigo_referido\": \"[codigo_referido]\",\n  \"incentivo_referidor\": \"[incentivo_referidor]\",\n  \"incentivo_referido\": \"[incentivo_referido]\",\n  \"canal_preferido\": \"[canal_preferido]\",\n  \"fecha_hora_envio_solicitud\": \"[fecha_hora_envio_solicitud]\",\n  \"contacto_referido_id\": \"[contacto_referido_id]\",\n  \"nombre_referido\": \"[nombre_referido]\",\n  \"fecha_hora_uso_codigo\": \"[fecha_hora_uso_codigo]\",\n  \"referido_inscrito\": \"[referido_inscrito]\",\n  \"fecha_inscripcion\": \"[fecha_inscripcion]\",\n  \"fecha_hora_notificacion\": \"[fecha_hora_notificacion]\",\n  \"usos_totales\": \"[usos_totales]\",\n  \"referidos_convertidos\": \"[referidos_convertidos]\",\n  \"estado_solicitud\": \"[estado_solicitud]\",\n  \"resultado\": \"[resultado]\",\n  \"fecha_hora_cierre\": \"[fecha_hora_cierre]\",\n  \"timestamp_registro\": \"[tiempo_fecha_actual]\"\n}\n```\n\n**Ejemplo de registro completo (solicitud enviada):**\n\n```json\n{\n  \"registro_id\": \"REF-REG-20260823143500\",\n  \"agente\": \"referral-request-agent\",\n  \"referal_request_id\": \"REF-REQ-20260823-143000-001\",\n  \"contacto_id\": \"CT-2026-00452\",\n  \"nombre_miembro\": \"María López\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"evento_tipo\": \"resena_positiva\",\n  \"programa_codigo\": \"descuento_membresia\",\n  \"programa_nombre\": \"Descuento en membresía\",\n  \"codigo_referido\": \"MARIA-LOPEZ-2026\",\n  \"incentivo_referidor\": \"10% de descuento en la próxima mensualidad\",\n  \"incentivo_referido\": \"15% de descuento en la primera mensualidad\",\n  \"canal_preferido\": \"whatsapp\",\n  \"fecha_hora_envio_solicitud\": \"2026-08-23T14:35:00Z\",\n  \"contacto_referido_id\": null,\n  \"nombre_referido\": null,\n  \"fecha_hora_uso_codigo\": null,\n  \"referido_inscrito\": false,\n  \"fecha_inscripcion\": null,\n  \"fecha_hora_notificacion\": null,\n  \"usos_totales\": 0,\n  \"referidos_convertidos\": 0,\n  \"estado_solicitud\": \"solicitud_enviada\",\n  \"resultado\": \"solicitud_enviada\",\n  \"fecha_hora_cierre\": null,\n  \"timestamp_registro\": \"2026-08-23T14:35:00Z\"\n}\n```\n\n**Ejemplo de registro completo (referido concretado):**\n\n```json\n{\n  \"registro_id\": \"REF-REG-20260825100500\",\n  \"agente\": \"referral-request-agent\",\n  \"referal_request_id\": \"REF-REQ-20260823-143000-001\",\n  \"contacto_id\": \"CT-2026-00452\",\n  \"nombre_miembro\": \"María López\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"evento_tipo\": \"codigo_utilizado\",\n  \"programa_codigo\": \"descuento_membresia\",\n  \"programa_nombre\": \"Descuento en membresía\",\n  \"codigo_referido\": \"MARIA-LOPEZ-2026\",\n  \"incentivo_referidor\": \"10% de descuento en la próxima mensualidad\",\n  \"incentivo_referido\": \"15% de descuento en la primera mensualidad\",\n  \"canal_preferido\": \"whatsapp\",\n  \"fecha_hora_envio_solicitud\": \"2026-08-23T14:35:00Z\",\n  \"contacto_referido_id\": \"CT-2026-00801\",\n  \"nombre_referido\": \"Ana García\",\n  \"fecha_hora_uso_codigo\": \"2026-08-25T10:00:00Z\",\n  \"referido_inscrito\": true,\n  \"fecha_inscripcion\": \"2026-08-25\",\n  \"fecha_hora_notificacion\": \"2026-08-25T10:05:00Z\",\n  \"usos_totales\": 1,\n  \"referidos_convertidos\": 1,\n  \"estado_solicitud\": \"referido_inscrito\",\n  \"resultado\": \"referido_concretado\",\n  \"fecha_hora_cierre\": \"2026-08-25T10:05:00Z\",\n  \"timestamp_registro\": \"2026-08-25T10:05:00Z\"\n}\n```\n\n**Ejemplo de registro completo (solicitud rechazada por el miembro):**\n\n```json\n{\n  \"registro_id\": \"REF-REG-20260824100000\",\n  \"agente\": \"referral-request-agent\",\n  \"referal_request_id\": \"REF-REQ-20260823-150000-002\",\n  \"contacto_id\": \"CT-2026-00389\",\n  \"nombre_miembro\": \"Roberto García\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"evento_tipo\": \"decima_asistencia_mes\",\n  \"programa_codigo\": \"clase_gratis\",\n  \"programa_nombre\": \"Clase gratis\",\n  \"codigo_referido\": \"ROBERTO-GARCIA-2026\",\n  \"incentivo_referidor\": \"1 clase gratis\",\n  \"incentivo_referido\": \"1 clase gratis\",\n  \"canal_preferido\": \"whatsapp\",\n  \"fecha_hora_envio_solicitud\": \"2026-08-23T15:05:00Z\",\n  \"contacto_referido_id\": null,\n  \"nombre_referido\": null,\n  \"fecha_hora_uso_codigo\": null,\n  \"referido_inscrito\": false,\n  \"fecha_inscripcion\": null,\n  \"fecha_hora_notificacion\": null,\n  \"usos_totales\": 0,\n  \"referidos_convertidos\": 0,\n  \"estado_solicitud\": \"rechazada_por_miembro\",\n  \"resultado\": \"rechazada\",\n  \"fecha_hora_cierre\": \"2026-08-24T10:00:00Z\",\n  \"timestamp_registro\": \"2026-08-24T10:00:00Z\"\n}\n```\n\n---\n\n## 6. Registro de escalamiento\n\n**Cuándo se genera:** Cuando el agente escala el caso a un humano.\n\n**Estructura:**\n\n```json\n{\n  \"escalamiento_id\": \"REF-ESC-[timestamp]\",\n  \"agente\": \"referral-request-agent\",\n  \"gimnasio_id\": \"[gimnasio_id]\",\n  \"gimnasio_nombre\": \"[gimnasio_nombre]\",\n  \"contacto_id\": \"[contacto_id]\",\n  \"nombre_miembro\": \"[nombre_miembro]\",\n  \"referal_request_id\": \"[referal_request_id]\",\n  \"codigo_referido\": \"[codigo_referido]\",\n  \"motivo_escalamiento\": \"[motivo_enum]\",\n  \"descripcion_caso\": \"[texto_descriptivo]\",\n  \"urgencia\": \"[alta | media | baja]\",\n  \"asignado_a\": \"[nombre_humano_asignado]\",\n  \"timestamp_escalamiento\": \"[tiempo_fecha_actual]\",\n  \"datos_contexto\": {\n    \"evento_tipo\": \"[evento_tipo]\",\n    \"programa_codigo\": \"[programa_codigo]\",\n    \"solicitudes_previas\": \"[solicitudes_referido_previas]\",\n    \"estado_solicitud\": \"[estado_solicitud]\"\n  }\n}\n```\n\n**Ejemplo de registro de escalamiento:**\n\n```json\n{\n  \"escalamiento_id\": \"REF-ESC-20260825110000\",\n  \"agente\": \"referral-request-agent\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"contacto_id\": \"CT-2026-00601\",\n  \"nombre_miembro\": \"Pedro Sánchez\",\n  \"referal_request_id\": \"REF-REQ-20260823-160000-003\",\n  \"codigo_referido\": \"PEDRO-SANCHEZ-2026\",\n  \"motivo_escalamiento\": \"problema_codigo_referido\",\n  \"descripcion_caso\": \"El miembro Pedro Sánchez reportó que su código de referido no funciona. Cuando sus amigos intentan usarlo, reciben un error 404. El enlace https://app.fitzone.com/r/PEDRO-SANCHEZ-2026 no carga. El miembro ya compartió el código con 3 personas y todas tuvieron el mismo problema.\",\n  \"urgencia\": \"media\",\n  \"asignado_a\": \"Equipo Técnico FitZone Centro\",\n  \"timestamp_escalamiento\": \"2026-08-25T11:00:00Z\",\n  \"datos_contexto\": {\n    \"evento_tipo\": \"resena_positiva\",\n    \"programa_codigo\": \"clase_gratis\",\n    \"solicitudes_previas\": 1,\n    \"estado_solicitud\": \"solicitud_enviada\"\n  }\n}\n```\n\n---\n\n## 7. Formato de fechas\n\n| Campo | Formato | Ejemplo |\n|---|---|---|\n| Fecha de evento | `DD/MM/YYYY HH:MM` | `\"23/08/2026 12:00\"` |\n| Fecha de código | `DD/MM/YYYY` | `\"23/08/2026\"` |\n| Fecha de expiración | `DD/MM/YYYY` | `\"22/09/2026\"` |\n| Fecha ISO 8601 | `YYYY-MM-DDTHH:MM:SS` | `\"2026-08-23T14:30:00Z\"` |\n| Hora | `HH:MM` | `\"14:30\"` |\n\n---\n*Plantilla generada para el nicho Gimnasios y Fitness — Agente Referral Request Agent*\n"
              },
              {
                "id": "referral-request-agent/prompt-sistema.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "prompt-sistema.md",
                "contenido": "# Prompt del Sistema — Referral Request Agent\n\n## Identidad\nEres **Referral Request Agent**, un asistente automatizado de **[Nombre del Gimnasio]** especializado en solicitar referidos a miembros satisfechos. Tu función es ejecutar el flujo de 5 pasos exactos que se define a continuación.\n\n## Tono de comunicación\n- Cercano y personal — el mensaje se siente como un pedido de un amigo\n- Generoso — el incentivo se presenta como un beneficio para ambos\n- Sin presionar — máximo 2 mensajes de solicitud\n- Orientado a la comunidad — compartir algo bueno con alguien que te importa\n\n## Flujo de ejecución\n\nEl agente ejecuta estos 5 pasos en orden, sin bifurcaciones ni condicionales:\n\n### Paso 1: Detecta un evento de satisfacción confirmado\nEl agente detecta un evento que indica satisfacción: reseña positiva, clase completada, evaluación física, 10ª asistencia al mes, membresía renovada o solicitud manual.\n\n### Paso 2: Envía un mensaje solicitando referidos con el incentivo definido\nEl agente envía el mensaje con código de referido, enlace directo e incentivos para ambos (referidor y referido).\n\n### Paso 3: Genera o adjunta el enlace/código de referido único del contacto\nEl agente verifica que el código sea único, accesible y que el programa de referidos esté activo.\n\n### Paso 4: Registra cuando el enlace/código sea utilizado por un nuevo contacto\nEl agente detecta y registra cuando un nuevo contacto usa el código de referido.\n\n### Paso 5: Notifica al equipo humano cuando se concrete un referido\nEl agente notifica al equipo y envía agradecimiento al referidor cuando el referido completa la inscripción.\n\n## Ejemplo de ejecución\n\n**Entrada:**\n```\nevento_tipo: \"resena_positiva\"\ncontacto_id: \"CT-2026-00452\"\nnombre_miembro: \"María López\"\n```\n\n**Ejecución paso a paso:**\n\n**Paso 1:** Detecta reseña de 5 estrellas de María López en Google. Verifica 53 días de membresía, sin solicitud en 30 días, programa activo.\n\n**Paso 2:** Envía mensaje:\n```\n¡Hola María López! Nos alegra que estés disfrutando de FitZone Centro 💪\n¿Conocés a alguien que le gustaría entrenar con nosotros?\n🎁 Para vos: 10% de descuento en la próxima mensualidad\n🎁 Para tu amigo/a: 15% de descuento en la primera mensualidad\nTu código: MARIA-LOPEZ-2026\n👉 https://app.fitzone.com/r/MARIA-LOPEZ-2026\n```\n\n**Paso 3:** Verifica código único y enlace accesible.\n\n**Paso 4:** Ana García usa el código → Se registra uso.\n\n**Paso 5:** Ana se inscribe → Notifica al equipo y envía agradecimiento a María.\n\n## Programas de referidos (5 tipos)\n\n| Código | Programa | Incentivo referidor | Incentivo referido |\n|--------|----------|---------------------|-------------------|\n| `clase_gratis` | Clase gratis | 1 clase gratis | 1 clase gratis |\n| `descuento_membresia` | Descuento membresía | 10% descuento próxima mensualidad | 15% descuento primera mensualidad |\n| `mes_gratis` | Mes gratis | 1 mes gratis | 1 mes gratis |\n| `producto_regalo` | Producto de regalo | Producto de tienda | Producto de tienda |\n| `sin_incentivo` | Sin incentivo | Solo agradecimiento | Solo acceso |\n\n## Eventos de satisfacción que activan el flujo (6 tipos)\n\n| Evento | Intervalo de espera |\n|--------|---------------------|\n| Reseña positiva (5 estrellas) | 2 horas después |\n| Clase completada (primera vez) | 2 horas después |\n| Evaluación física completada | 4 horas después |\n| 10ª asistencia al mes | 24 horas después |\n| Membresía renovada | 48 horas después |\n| Solicitud manual | Inmediatamente |\n\n## Restricciones\n1. Ejecutar solo los 5 pasos del flujo, sin agregar pasos adicionales\n2. Máximo 2 mensajes de solicitud por miembro en 30 días\n3. Esperar al menos 14 días de membresía antes de solicitar referidos\n4. Siempre incluir enlace o código de referido de forma clara\n5. Incluir incentivo explícito para referidor y referido\n6. No presionar — si no desea referir, registrar como `rechazada` y cerrar\n7. Verificar que el programa de referidos esté activo antes de enviar\n\n## Reglas de escalamiento\nEl agente escala a un humano en las siguientes situaciones:\n\n| Condición | Tiempo de respuesta |\n|-----------|---------------------|\n| Miembro reporta problema con el código de referido | 1 hora |\n| Miembro reporta que no recibió el incentivo prometido | 4 horas |\n| Error en el envío de mensajes (3 intentos fallidos) | 1 hora |\n| Código de referido duplicado detectado | 30 minutos |\n| Programa de referidos inactivo o con problemas | 24 horas |\n| Miembro solicita eliminar su código de referido | 24 horas |\n"
              },
              {
                "id": "referral-request-agent/reglas-escalamiento.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "reglas-escalamiento.md",
                "contenido": "# Reglas de escalamiento — Referral Request Agent\n\n## Descripción\n\nLista las condiciones exactas bajo las cuales el agente debe transferir el caso a un humano, sin dejar casos ambiguos. Cada condición tiene un identificador único, la situación que la activa, la acción a tomar y el plazo de respuesta.\n\n---\n\n## Condiciones de escalamiento\n\n### Categoría 1: Problemas con el código o enlace de referido\n\n| ID | Condición | Acción del agente | Urgencia | Plazo de respuesta |\n|----|-----------|-------------------|----------|-------------------|\n| C1.1 | El miembro reporta que su código de referido no funciona (error 404, enlace roto) | Escalar a [equipo técnico] con los detalles del error. No generar un nuevo código hasta resolver. | Media | 24 horas |\n| C1.2 | El código de referido ya está en uso por otro miembro al intentar generarlo | Escalar a [equipo de datos] para verificar la duplicidad. Generar código temporal con formato `[NOMBRE]-[AÑO]-[SECUENCIA]` mientras se resuelve. | Baja | 48 horas |\n| C1.3 | El miembro reporta que recibió un código que no le corresponde | Escalar a [equipo técnico] para verificar la asignación. No generar nuevo código hasta resolver. | Media | 24 horas |\n\n### Categoría 2: Problemas con los incentivos\n\n| ID | Condición | Acción del agente | Urgencia | Plazo de respuesta |\n|----|-----------|-------------------|----------|-------------------|\n| C2.1 | El miembro reportó que el referido no recibió su incentivo | Escalar a [departamento comercial/finanzas] para verificar el proceso de entrega de incentivos. | Media | 24 horas |\n| C2.2 | El miembro reportó que no recibió su propio incentivo después de un referido concretado | Escalar a [departamento comercial/finanzas] con los datos del referido y el código utilizado. | Media | 24 horas |\n| C2.3 | El programa de referidos cambió sus incentivos después de que se enviara la solicitud | No escalar. El miembro recibe los incentivos que estaban vigentes al momento de la solicitud. Informar al responsable del programa. | Baja | 48 horas |\n\n### Categoría 3: Problemas técnicos de envío\n\n| ID | Condición | Acción del agente | Urgencia | Plazo de respuesta |\n|----|-----------|-------------------|----------|-------------------|\n| C3.1 | El envío del mensaje de solicitud falla dos veces consecutivas | Registrar el error. Escalar a [equipo técnico] para verificar el canal de comunicación. El caso queda pendiente. | Media | 24 horas |\n| C3.2 | El canal de comunicación preferido del miembro no está configurado o es inválido | Escalar a [equipo técnico] para verificar la configuración. Usar `email` como canal de respaldo si está disponible. | Baja | 48 horas |\n\n### Categoría 4: Datos del miembro incompletos o inválidos\n\n| ID | Condición | Acción del agente | Urgencia | Plazo de respuesta |\n|----|-----------|-------------------|----------|-------------------|\n| C4.1 | El `email_responsable` o `email_miembro` no tiene formato válido | Escalar a [equipo de datos] para verificar el registro. No enviar mensajes por email hasta resolver. | Baja | 48 horas |\n| C4.2 | El `telefono_miembro` no tiene formato válido y el canal es `whatsapp` o `sms` | Escalar a [equipo de datos] para verificar. Si el email es válido, usar email como respaldo. | Baja | 48 horas |\n| C4.3 | El miembro no existe en la base de datos del CRM después del evento | No iniciar flujo. Escalar a [equipo de datos] para verificar la integridad del sistema. | Media | 24 horas |\n\n### Categoría 5: Solicitudes especiales del miembro\n\n| ID | Condición | Acción del agente | Urgencia | Plazo de respuesta |\n|----|-----------|-------------------|----------|-------------------|\n| C5.1 | El miembro solicita un programa de referidos diferente al configurado | Escalar a [departamento comercial] para evaluar si se puede ofrecer un programa alternativo. | Baja | 48 horas |\n| C5.2 | El miembro reporta que alguien más usó su código sin su autorización | Escalar a [seguridad + departamento comercial] para verificar el uso no autorizado y proteger la cuenta. | Alta | 4 horas |\n| C5.3 | El miembro solicita desactivar su código de referido permanentemente | Registrar la solicitud y escalar a [departamento de datos] para desactivar el código. No reactivar sin solicitud expresa. | Baja | 48 horas |\n\n### Categoría 6: Errores del sistema o datos inconsistentes\n\n| ID | Condición | Acción del agente | Urgencia | Plazo de respuesta |\n|----|-----------|-------------------|----------|-------------------|\n| C6.1 | El `evento_id` recibido tiene formato inválido | No procesar. Escalar a [equipo técnico] para verificar el sistema de eventos. | Baja | 48 horas |\n| C6.2 | El mismo miembro recibe más de 3 solicitudes de referido en 30 días | No enviar más solicitudes. Escalar a [equipo técnico] para verificar el sistema de control de frecuencia. | Media | 24 horas |\n| C6.3 | Un referido se registra pero el `contacto_referido_id` coincide con un miembro existente | No procesar como referido. Escalar a [equipo de datos] para verificar si es re-registro o error. | Media | 24 horas |\n\n---\n\n## Reglas de asignación de urgencia\n\n| Nivel | Color | Significado | Tiempo máximo de espera |\n|-------|-------|-------------|------------------------|\n| **Alta** | 🔴 | Requiere intervención humana inmediata para evitar fraude o pérdida de datos del miembro | 4 horas |\n| **Media** | 🟡 | Requiere revisión humana para resolver la situación correctamente | 24 horas |\n| **Baja** | 🟢 | Informativo, requiere seguimiento pero no bloquea el programa de referidos | 48 horas |\n\n---\n\n## Reglas de notificación por urgencia\n\n| Urgencia | Canal de notificación | Destinatario |\n|----------|----------------------|--------------|\n| **Alta** | WhatsApp + Email | [Responsable directo] + [Supervisor] |\n| **Media** | Email | [Responsable directo] |\n| **Baja** | Registro en sistema | [Equipo de datos] |\n\n---\n\n## Textos base para escalamiento\n\n### Al miembro (cuando se escala):\n\n```\nHola [nombre_miembro], tu consulta requiere atención personalizada de nuestro equipo.\n\nUn miembro de [gimnasio_nombre] te contactará en las próximas horas para ayudarte.\n\nSi tenés alguna duda, escribinos por este canal.\n— [gimnasio_nombre]\n```\n\n**Ejemplo:**\n\n```\nHola Pedro Sánchez, tu consulta requiere atención personalizada de nuestro equipo.\n\nUn miembro de FitZone Centro te contactará en las próximas horas para ayudarte.\n\nSi tenés alguna duda, escribinos por este canal.\n— FitZone Centro\n```\n\n### Al equipo interno (notificación de escalamiento):\n\n```\n🚨 ESCALAMIENTO — Referral Request Agent\n\n📋 Caso: [escalamiento_id]\n👤 Miembro: [nombre_miembro] ([contacto_id])\n🔗 Código: [codigo_referido]\n📋 Programa: [programa_codigo]\n\n⚠️ Motivo: [motivo_escalamiento]\n📝 Detalle: [descripcion_caso]\n🔴 Urgencia: [urgencia]\n⏰ Plazo de respuesta: [plazo] horas\n👤 Asignado a: [asignado_a]\n\n📊 Contexto:\n- Evento disparador: [evento_tipo]\n- Solicitudes previas: [solicitudes_previas]\n- Estado actual: [estado_solicitud]\n```\n\n**Ejemplo:**\n\n```\n🚨 ESCALAMIENTO — Referral Request Agent\n\n📋 Caso: REF-ESC-20260825110000\n👤 Miembro: Pedro Sánchez (CT-2026-00601)\n🔗 Código: PEDRO-SANCHEZ-2026\n📋 Programa: clase_gratis\n\n⚠️ Motivo: problema_codigo_referido\n📝 Detalle: El miembro Pedro Sánchez reportó que su código de referido no funciona. Cuando sus amigos intentan usarlo, reciben un error 404. El enlace https://app.fitzone.com/r/PEDRO-SANCHEZ-2026 no carga. El miembro ya compartió el código con 3 personas y todas tuvieron el mismo problema.\n🔴 Urgencia: media\n⏰ Plazo de respuesta: 24 horas\n👤 Asignado a: Equipo Técnico FitZone Centro\n\n📊 Contexto:\n- Evento disparador: resena_positiva\n- Solicitudes previas: 1\n- Estado actual: solicitud_enviada\n```\n\n---\n\n## Ejemplo completo de escalamiento\n\n**Caso:** Pedro Sánchez reporta que su código de referido no funciona.\n\n**Registro de escalamiento generado:**\n\n```json\n{\n  \"escalamiento_id\": \"REF-ESC-20260825110000\",\n  \"agente\": \"referral-request-agent\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"contacto_id\": \"CT-2026-00601\",\n  \"nombre_miembro\": \"Pedro Sánchez\",\n  \"referal_request_id\": \"REF-REQ-20260823-160000-003\",\n  \"codigo_referido\": \"PEDRO-SANCHEZ-2026\",\n  \"motivo_escalamiento\": \"problema_codigo_referido\",\n  \"descripcion_caso\": \"El miembro Pedro Sánchez reportó que su código de referido no funciona. Cuando sus amigos intentan usarlo, reciben un error 404. El enlace https://app.fitzone.com/r/PEDRO-SANCHEZ-2026 no carga. El miembro ya compartió el código con 3 personas y todas tuvieron el mismo problema.\",\n  \"urgencia\": \"media\",\n  \"asignado_a\": \"Equipo Técnico FitZone Centro\",\n  \"timestamp_escalamiento\": \"2026-08-25T11:00:00Z\",\n  \"datos_contexto\": {\n    \"evento_tipo\": \"resena_positiva\",\n    \"programa_codigo\": \"clase_gratis\",\n    \"solicitudes_previas\": 1,\n    \"estado_solicitud\": \"solicitud_enviada\"\n  }\n}\n```\n\n**Acciones tomadas:**\n1. Se envió mensaje a Pedro Sánchez indicando que un humano lo contactará.\n2. Se notificó al Equipo Técnico por email.\n3. Se pausó la distribución del código hasta resolver el problema del enlace.\n4. El caso quedó marcado como `escorado` hasta recibir instrucción del técnico.\n\n---\n*Plantilla generada para el nicho Gimnasios y Fitness — Agente Referral Request Agent*\n"
              },
              {
                "id": "referral-request-agent/variables-entrada.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "variables-entrada.md",
                "contenido": "# Variables de entrada — Referral Request Agent\n\nEste archivo define la lista fija y cerrada de campos de datos que el agente necesita recibir para operar. No se admiten campos adicionales a los listados aquí.\n\n---\n\n## Campos del evento disparador\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 1 | `evento_id` | `string` | Obligatorio | Identificador único del evento. Formato: `REF-EVT-YYYYMMDD-HHMMSS-XXX`. | `\"REF-EVT-20260823-143000-001\"` |\n| 2 | `contacto_id` | `string` | Obligatorio | Identificador del contacto asociado al evento. Formato: `CT-YYYY-XXXXX`. | `\"CT-2026-00452\"` |\n| 3 | `evento_tipo` | `string` (enum) | Obligatorio | Tipo de evento que activa el flujo. Valores: `resena_positiva`, `clase_completada`, `evaluacion_completada`, `decima_asistencia_mes`, `membresia_renovada`, `solicitud_manual`. | `\"resena_positiva\"` |\n| 4 | `evento_detalle` | `string` | Opcional | Detalle adicional del evento. | `\"Reseña de 5 estrellas en Google\"` |\n| 5 | `fecha_hora_evento` | `string` (ISO 8601) | Obligatorio | Fecha y hora en que ocurrió el evento. | `\"2026-08-23T12:00:00Z\"` |\n\n---\n\n## Datos del miembro (referidor)\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 6 | `nombre_miembro` | `string` | Obligatorio | Nombre completo del miembro. | `\"María López\"` |\n| 7 | `telefono_miembro` | `string` | Opcional (requerido si `canal_preferido` es `whatsapp` o `sms`) | Número telefónico del miembro. Entre 8 y 15 dígitos con código de país. | `\"+54 11 5555-1234\"` |\n| 8 | `email_miembro` | `string` | Opcional (requerido si `canal_preferido` es `email`) | Correo electrónico del miembro. | `\"maria.lopez@email.com\"` |\n| 9 | `canal_preferido` | `string` (enum) | Obligatorio | Canal preferido para comunicación. Valores: `whatsapp`, `sms`, `email`. | `\"whatsapp\"` |\n\n---\n\n## Datos de la membresía\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 10 | `membresia_tipo` | `string` (enum) | Obligatorio | Tipo de membresía. Valores: `premium`, `basico`, `estudiante`, `corporativo`. | `\"premium\"` |\n| 11 | `membresia_estado` | `string` (enum) | Obligatorio | Estado de la membresía. Solo se procesan membresías con estado `activa`. | `\"activa\"` |\n| 12 | `membresia_fecha_inicio` | `string` (YYYY-MM-DD) | Obligatorio | Fecha de inicio de la membresía. | `\"2026-07-01\"` |\n| 13 | `dias_membresia` | `integer` | Obligatorio | Días de membresía activa del miembro. | `53` |\n\n---\n\n## Programa de referidos\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 14 | `programa_referidos` | `object` | Obligatorio | Configuración del programa de referidos activo. | Ver estructura abajo |\n\n**Estructura de `programa_referidos`:**\n\n| Campo | Tipo | Descripción | Ejemplo |\n|---|---|---|---|\n| `codigo` | `string` (enum) | Código del programa. Valores: `clase_gratis`, `descuento_membresia`, `mes_gratis`, `producto_regalo`, `sin_incentivo`. | `\"descuento_membresia\"` |\n| `nombre` | `string` | Nombre legible del programa. | `\"Descuento en membresía\"` |\n| `incentivo_referidor` | `string` | Beneficio para el miembro que refiere. | `\"10% de descuento en la próxima mensualidad\"` |\n| `incentivo_referido` | `string` | Beneficio para el nuevo miembro referido. | `\"15% de descuento en la primera mensualidad\"` |\n| `validez_dias` | `integer` | Días de validez del código de referido. | `30` |\n| `estado` | `string` (enum) | Estado del programa. Valores: `activo`, `inactivo`, `pausado`. | `\"activo\"` |\n\n---\n\n## Código de referido\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 15 | `codigo_referido_asignado` | `string` | Obligatorio | Código de referido único asignado al miembro. | `\"MARIA-LOPEZ-2026\"` |\n| 16 | `enlace_referido` | `string` | Obligatorio | URL del enlace de referido. | `\"https://app.fitzone.com/r/MARIA-LOPEZ-2026\"` |\n| 17 | `solicitudes_referido_previas` | `integer` | Obligatorio (default: 0) | Número de solicitudes de referido enviadas previamente a este miembro. | `0` |\n\n---\n\n## Datos del referido (cuando se usa el código)\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 18 | `contacto_referido_id` | `string` | Condicional (requerido si `evento_tipo` es `codigo_utilizado`) | ID del nuevo contacto que usó el código. | `\"CT-2026-00801\"` |\n| 19 | `nombre_referido` | `string` | Condicional (requerido si `evento_tipo` es `codigo_utilizado`) | Nombre del referido. | `\"Ana García\"` |\n| 20 | `telefono_referido` | `string` | Opcional | Teléfono del referido. | `\"+54 11 8888-1234\"` |\n| 21 | `email_referido` | `string` | Opcional | Email del referido. | `\"ana.garcia@email.com\"` |\n| 22 | `fecha_hora_uso_codigo` | `string` (ISO 8601) | Condicional (requerido si `evento_tipo` es `codigo_utilizado`) | Fecha y hora en que se usó el código. | `\"2026-08-25T10:00:00Z\"` |\n| 23 | `referido_inscrito` | `boolean` | Condicional (requerido si `evento_tipo` es `codigo_utilizado`) | Si el referido completó la inscripción. | `true` |\n| 24 | `fecha_inscripcion` | `string` (YYYY-MM-DD) | Condicional (requerido si `referido_inscrito` es `true`) | Fecha de inscripción del referido. | `\"2026-08-25\"` |\n| 25 | `membresia_tipo_referido` | `string` (enum) | Condicional (requerido si `referido_inscrito` es `true`) | Tipo de membresía del referido. | `\"premium\"` |\n\n---\n\n## Datos del gimnasio\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 26 | `gimnasio_id` | `string` | Obligatorio | Identificador único del gimnasio. | `\"GYM-001\"` |\n| 27 | `gimnasio_nombre` | `string` | Obligatorio | Nombre comercial del gimnasio. | `\"FitZone Centro\"` |\n| 28 | `gimnasio_telefono` | `string` | Opcional | Teléfono del gimnasio. | `\"+54 11 4444-5678\"` |\n\n---\n\n## Datos de notificación\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 29 | `responsable_programa` | `string` | Obligatorio | Nombre del responsable del programa de referidos. | `\"Departamento de Marketing\"` |\n| 30 | `email_responsable` | `string` | Obligatorio | Correo electrónico del responsable. | `\"marketing@fitzone.com\"` |\n| 31 | `canal_notificacion_equipo` | `string` (enum) | Opcional (default: `\"email\"`) | Canal para notificar al equipo. Valores: `email`, `app`. | `\"email\"` |\n\n---\n\n## Datos de tiempos\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 32 | `tiempo_fecha_actual` | `string` (ISO 8601) | Obligatorio | Fecha y hora actual del sistema. | `\"2026-08-23T14:30:00Z\"` |\n| 33 | `horario_atencion_inicio` | `string` (HH:MM) | Opcional | Hora de inicio del horario de atención. Default: `\"07:00\"`. | `\"07:00\"` |\n| 34 | `horario_atencion_fin` | `string` (HH:MM) | Opcional | Hora de fin del horario de atención. Default: `\"22:00\"`. | `\"22:00\"` |\n| 35 | `min_dias_membresia_para_referir` | `integer` | Opcional (default: 14) | Días mínimos de membresía antes de solicitar referidos. | `14` |\n| 36 | `dias_entre_solicitudes` | `integer` | Opcional (default: 30) | Días mínimos entre solicitudes de referido al mismo miembro. | `30` |\n\n---\n\n## Campos calculados por el agente\n\nEstos campos **no se reciben de entrada**; el agente los genera internamente.\n\n| # | Nombre del campo | Tipo de dato | Generado en | Descripción |\n|---|---|---|---|---|\n| 37 | `referal_request_id` | `string` | Paso 1 | ID único de la solicitud. Formato: `REF-REQ-YYYYMMDD-HHMMSS-XXX`. |\n| 38 | `fecha_hora_deteccion_evento` | `string` (ISO 8601) | Paso 1 | Marca de tiempo de detección. |\n| 39 | `fecha_hora_envio_solicitud` | `string` (ISO 8601) o `null` | Paso 2 | Marca de envío de la solicitud. |\n| 40 | `fecha_expiracion_codigo` | `string` (ISO 8601) | Paso 3 | Fecha de expiración del código. |\n| 41 | `usos_totales` | `integer` | Paso 4 | Total de veces que se usó el código. |\n| 42 | `referidos_convertidos` | `integer` | Paso 5 | Total de referidos que completaron la inscripción. |\n| 43 | `fecha_hora_notificacion` | `string` (ISO 8601) o `null` | Paso 5 | Marca de notificación al equipo. |\n| 44 | `estado_solicitud` | `string` (enum) | Paso 5 | Estado de la solicitud. |\n| 45 | `resultado` | `string` (enum) | Paso 5 | Resultado final. |\n| 46 | `fecha_hora_cierre` | `string` (ISO 8601) | Paso 5 | Marca de cierre. |\n\n---\n\n## Validaciones de entrada\n\n| Campo | Regla | Acción si falla |\n|---|---|---|\n| `evento_id` | No vacío, formato válido | Terminar flujo sin ejecutar |\n| `contacto_id` | Debe seguir formato `CT-YYYY-XXXXX` | Terminar flujo sin ejecutar |\n| `evento_tipo` | Debe ser uno de los 6 valores válidos | Terminar flujo sin ejecutar |\n| `nombre_miembro` | No vacío, mínimo 2 caracteres | Terminar flujo sin ejecutar |\n| `membresia_tipo` | Debe ser uno de los 4 valores válidos | Terminar flujo sin ejecutar |\n| `membresia_estado` | Debe ser `activa` | No solicitar referidos |\n| `dias_membresia` | Debe ser >= `min_dias_membresia_para_referir` | No solicitar, registrar como `miembro_nuevo` |\n| `solicitudes_referido_previas` | Debe ser < 2 (máximo 2 solicitudes en 30 días) | No solicitar, registrar como `limite_alcanzado` |\n| `programa_referidos.estado` | Debe ser `activo` | No solicitar, registrar como `programa_inactivo` |\n| `codigo_referido_asignado` | No vacío | Terminar flujo sin ejecutar |\n| `enlace_referido` | No vacío, formato URL válido | Generar enlace default |\n| `gimnasio_id` | No vacío | Terminar flujo sin ejecutar |\n| `responsable_programa` | No vacío | Terminar flujo sin ejecutar |\n| `email_responsable` | Formato de email válido | Escalar a humano (C4.1) |\n\n---\n\n## Ejemplo de payload completo (evento de solicitud)\n\n```json\n{\n  \"evento_id\": \"REF-EVT-20260823-143000-001\",\n  \"contacto_id\": \"CT-2026-00452\",\n  \"evento_tipo\": \"resena_positiva\",\n  \"evento_detalle\": \"Reseña de 5 estrellas en Google\",\n  \"fecha_hora_evento\": \"2026-08-23T12:00:00Z\",\n  \"nombre_miembro\": \"María López\",\n  \"telefono_miembro\": \"+54 11 5555-1234\",\n  \"email_miembro\": \"maria.lopez@email.com\",\n  \"canal_preferido\": \"whatsapp\",\n  \"membresia_tipo\": \"premium\",\n  \"membresia_estado\": \"activa\",\n  \"membresia_fecha_inicio\": \"2026-07-01\",\n  \"dias_membresia\": 53,\n  \"programa_referidos\": {\n    \"codigo\": \"descuento_membresia\",\n    \"nombre\": \"Descuento en membresía\",\n    \"incentivo_referidor\": \"10% de descuento en la próxima mensualidad\",\n    \"incentivo_referido\": \"15% de descuento en la primera mensualidad\",\n    \"validez_dias\": 30,\n    \"estado\": \"activo\"\n  },\n  \"codigo_referido_asignado\": \"MARIA-LOPEZ-2026\",\n  \"enlace_referido\": \"https://app.fitzone.com/r/MARIA-LOPEZ-2026\",\n  \"solicitudes_referido_previas\": 0,\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"gimnasio_telefono\": \"+54 11 4444-5678\",\n  \"responsable_programa\": \"Departamento de Marketing\",\n  \"email_responsable\": \"marketing@fitzone.com\",\n  \"canal_notificacion_equipo\": \"email\",\n  \"horario_atencion_inicio\": \"07:00\",\n  \"horario_atencion_fin\": \"22:00\",\n  \"tiempo_fecha_actual\": \"2026-08-23T14:30:00Z\",\n  \"min_dias_membresia_para_referir\": 14,\n  \"dias_entre_solicitudes\": 30\n}\n```\n\n## Ejemplo de payload completo (código utilizado)\n\n```json\n{\n  \"evento_id\": \"REF-USE-20260825-100000-001\",\n  \"contacto_id\": \"CT-2026-00452\",\n  \"evento_tipo\": \"codigo_utilizado\",\n  \"evento_detalle\": \"Código MARIA-LOPEZ-2026 usado por Ana García\",\n  \"fecha_hora_evento\": \"2026-08-25T10:00:00Z\",\n  \"nombre_miembro\": \"María López\",\n  \"telefono_miembro\": \"+54 11 5555-1234\",\n  \"email_miembro\": \"maria.lopez@email.com\",\n  \"canal_preferido\": \"whatsapp\",\n  \"membresia_tipo\": \"premium\",\n  \"membresia_estado\": \"activa\",\n  \"membresia_fecha_inicio\": \"2026-07-01\",\n  \"dias_membresia\": 55,\n  \"programa_referidos\": {\n    \"codigo\": \"descuento_membresia\",\n    \"nombre\": \"Descuento en membresía\",\n    \"incentivo_referidor\": \"10% de descuento en la próxima mensualidad\",\n    \"incentivo_referido\": \"15% de descuento en la primera mensualidad\",\n    \"validez_dias\": 30,\n    \"estado\": \"activo\"\n  },\n  \"codigo_referido_asignado\": \"MARIA-LOPEZ-2026\",\n  \"enlace_referido\": \"https://app.fitzone.com/r/MARIA-LOPEZ-2026\",\n  \"solicitudes_referido_previas\": 1,\n  \"contacto_referido_id\": \"CT-2026-00801\",\n  \"nombre_referido\": \"Ana García\",\n  \"telefono_referido\": \"+54 11 8888-1234\",\n  \"email_referido\": \"ana.garcia@email.com\",\n  \"fecha_hora_uso_codigo\": \"2026-08-25T10:00:00Z\",\n  \"referido_inscrito\": true,\n  \"fecha_inscripcion\": \"2026-08-25\",\n  \"membresia_tipo_referido\": \"premium\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Centro\",\n  \"gimnasio_telefono\": \"+54 11 4444-5678\",\n  \"responsable_programa\": \"Departamento de Marketing\",\n  \"email_responsable\": \"marketing@fitzone.com\",\n  \"canal_notificacion_equipo\": \"email\",\n  \"horario_atencion_inicio\": \"07:00\",\n  \"horario_atencion_fin\": \"22:00\",\n  \"tiempo_fecha_actual\": \"2026-08-25T10:05:00Z\",\n  \"min_dias_membresia_para_referir\": 14,\n  \"dias_entre_solicitudes\": 30\n}\n```\n\n---\n*Plantilla generada para el nicho Gimnasios y Fitness — Agente Referral Request Agent*\n"
              }
            ]
          }
        ]
      },
      {
        "id": "check-in-follow-up",
        "carpeta": "check-in-follow-up-agent",
        "nombre": "Check-in Follow-up Agent",
        "icono": "✅",
        "descripcion": "Dar seguimiento después de la primera visita o check-in del nuevo miembro",
        "submodulos": [],
        "categoria": "general",
        "estructura": [
          {
            "id": "check-in-follow-up-agent",
            "etiqueta": "",
            "es_plantilla": true,
            "nombre": "check-in-follow-up-agent",
            "subcarpetas": [],
            "notas": [
              {
                "id": "check-in-follow-up-agent/flujo.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "flujo.md",
                "contenido": "# Flujo de ejecución — Check-in Follow-up Agent\n\nEste archivo documenta el flujo fijo de ejecución del agente. Cada check-in del miembro sigue estos 5 pasos **en orden**, sin pasos condicionales ni bifurcaciones.\n\n---\n\n## Paso 1 — Detección del evento disparador (check-in registrado)\n\nEl agente detecta que un miembro completó su check-in en recepción o a través de la app del gimnasio.\n\n**Acción del agente:**\n1. Recibir los datos del check-in según lo definido en `variables-entrada.md`.\n2. Registrar la marca de tiempo de detección en `fecha_hora_deteccion_checkin`.\n3. Validar que el `contacto_id` tenga un formato válido.\n4. Verificar que el `estado_cliente` sea `activo` (no suspendido ni de baja).\n5. Verificar que no exista un follow-up abierto previo para el mismo check-in.\n6. Determinar el tipo de seguimiento según la tabla de eventos:\n   - `CHECKIN_REGISTRADO`: intervalo 2 horas.\n   - `PRIMERA_VISITA`: intervalo 1 hora.\n   - `SESION_LARGA`: intervalo 1 hora.\n   - `SESION_CORTA`: intervalo 2 horas.\n   - `ASISTENCIA_IRREGULAR`: envío inmediato.\n7. Calcular el `momento_envio_seguimiento`: `fecha_hora_checkin` + intervalo.\n8. Verificar si el `momento_envio_seguimiento` cae dentro del horario de atención (07:00–22:00):\n   - Si es antes de las 07:00 → ajustar a las 10:00 del mismo día.\n   - Si es después de las 22:00 → ajustar a las 10:00 del siguiente día hábil.\n9. Registrar el `momento_envio_seguimiento_ajustado`.\n10. Generar el `followup_id` único.\n\n**Ejemplo de entrada (check-in registrado):**\n\n```json\n{\n  \"evento_id\": \"CHK-20260823-091500-001\",\n  \"contacto_id\": \"CON-20260115-101\",\n  \"evento_tipo\": \"CHECKIN_REGISTRADO\",\n  \"fecha_hora_checkin\": \"2026-08-23T09:15:00\",\n  \"nombre_miembro\": \"Ana García López\",\n  \"telefono_miembro\": \"+52 55 9876 5432\",\n  \"email_miembro\": \"ana.garcia@correo.com\",\n  \"canal_preferido\": \"whatsapp\",\n  \"estado_cliente\": \"activo\",\n  \"duracion_sesion_minutos\": 75,\n  \"area_utilizada\": \"Sala de peso libre\",\n  \"clase_asistida\": null,\n  \"instructor_nombre\": null,\n  \"checkins_previos_mes\": 8,\n  \"dias_desde_ultimo_checkin\": 2,\n  \"primera_vez\": false,\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"responsable_seguimiento\": \"Recepción\",\n  \"email_responsable\": \"recepcion@fitzone.com\",\n  \"horario_atencion_inicio\": \"07:00\",\n  \"horario_atencion_fin\": \"22:00\",\n  \"tiempo_fecha_actual\": \"2026-08-23T09:20:00\"\n}\n```\n\n---\n\n## Paso 2 — Espera del intervalo de tiempo configurado\n\nEl agente espera el intervalo de tiempo configurado antes de enviar el mensaje de seguimiento.\n\n**Acción del agente:**\n1. Determinar el intervalo según el tipo de evento (ver tabla en paso 1).\n2. Calcular `momento_envio_seguimiento` = `fecha_hora_checkin` + intervalo.\n3. Ajustar al horario de atención si es necesario.\n4. Esperar hasta que se alcance el momento de envío.\n5. Registrar `fecha_hora_inicio_espera`.\n\n**Tabla de intervalos:**\n\n| Tipo de evento | Intervalo | Ejemplo (check-in 09:15) |\n|---|---|---|\n| CHECKIN_REGISTRADO | 2 horas | Envío: 11:15 |\n| PRIMERA_VISITA | 1 hora | Envío: 10:15 |\n| SESION_LARGA | 1 hora | Envío: 10:15 |\n| SESION_CORTA | 2 horas | Envío: 11:15 |\n| ASISTENCIA_IRREGULAR | Inmediato | Envío: 09:15 |\n\n---\n\n## Paso 3 — Redacción del mensaje de seguimiento personalizado\n\nEl agente genera el mensaje de seguimiento usando la plantilla de `formato-salida.md` → sección 1, insertando los datos del check-in.\n\n**Acción del agente:**\n1. Seleccionar la plantilla según el tipo de evento.\n2. Insertar: nombre del miembro, fecha, duración, área, clase (si aplica), instructor (si aplica).\n3. Formatear la fecha en formato `DD/MM/YYYY HH:MM`.\n4. Generar el mensaje para el canal configurado.\n\n**Plantilla de seguimiento estándar (whatsapp/sms):**\n\n```\n¡Hola {{nombre_miembro}}! Nos alegra que nos visitaste hoy 💪\n\n📅 Check-in: {{fecha_checkin_formateada}}\n⏱️ Duración: {{duracion_sesion}} minutos\n🏋️ Área: {{area_utilizada}}\n\n¿Cómo fue tu experiencia? Si necesitás algo o tenés feedback, escribinos.\n— {{gimnasio_nombre}}\n```\n\n**Plantilla de seguimiento — primera visita:**\n\n```\n¡Hola {{nombre_miembro}}! Bienvenido/a por primera vez a {{gimnasio_nombre}} 🎉\n\n📅 Tu primera visita: {{fecha_checkin_formateada}}\n⏱️ Duración: {{duracion_sesion}} minutos\n🏋️ Área: {{area_utilizada}}\n\n¿Qué tal tu primera experiencia? Si tenés preguntas sobre nuestras instalaciones, clases o servicios, escribinos.\n— {{gimnasio_nombre}}\n```\n\n**Plantilla de seguimiento — sesión larga:**\n\n```\n¡Hola {{nombre_miembro}}! 💪 Vemos que tuviste una sesión intensa hoy.\n\n📅 Check-in: {{fecha_checkin_formateada}}\n⏱️ Duración: {{duracion_sesion}} minutos\n🏋️ Área: {{area_utilizada}}\n\nRecordá hidratarte bien y hacer estiramientos. ¿Cómo te sentiste?\n— {{gimnasio_nombre}}\n```\n\n**Plantilla de seguimiento — asistencia irregular:**\n\n```\n¡Hola {{nombre_miembro}}! Te extrañamos 💪\n\nLlevás {{dias_sin_asistir}} días sin visitarnos. ¿Todo bien?\n\nSi necesitás ayuda con tu rutina o tenés alguna consulta, escribinos. ¡Te esperamos!\n— {{gimnasio_nombre}}\n```\n\n**Ejemplo renderizado (estándar, whatsapp):**\n\n```\n¡Hola Ana García López! Nos alegra que nos visitaste hoy 💪\n\n📅 Check-in: 23/08/2026 09:15\n⏱️ Duración: 75 minutos\n🏋️ Área: Sala de peso libre\n\n¿Cómo fue tu experiencia? Si necesitás algo o tenés feedback, escribinos.\n— FitZone Premium\n```\n\n---\n\n## Paso 4 — Envío del mensaje por el canal configurado\n\nEl agente envía el mensaje de seguimiento al miembro por el canal preferido.\n\n**Acción del agente:**\n1. Seleccionar el canal de envío (`canal_preferido`).\n2. Enviar el mensaje generado en el paso 3.\n3. Si el envío falla, reintentar una sola vez después de 30 segundos.\n4. Si el segundo intento falla, registrar el error pero continuar (el caso se cierra como `envio_fallido`).\n5. Registrar `fecha_hora_envio_seguimiento`.\n6. Actualizar el estado del caso a `seguimiento_enviado`.\n7. Iniciar el contador de plazo de respuesta: **48 horas**.\n8. Calcular `momento_recordatorio`: `fecha_hora_envio_seguimiento` + 24 horas.\n\n---\n\n## Paso 5 — Registro de la respuesta del miembro y cierre del caso\n\nEl agente espera la respuesta del miembro y la registra en el historial.\n\n**Acción del agente:**\n1. Esperar la respuesta dentro del plazo de 48 horas.\n2. Si el miembro responde:\n   - Clasificar la respuesta (ver tabla).\n   - Registrar en el historial.\n   - Si es positiva o neutra → cerrar caso como `cerrado`.\n   - Si reporta problema → escalar a humano (paso 6).\n3. Si el plazo expira sin respuesta:\n   - Registrar como `sin_respuesta`.\n   - Si es el primer mensaje → enviar recordatorio (24h después).\n   - Si es el segundo mensaje → cerrar caso como `cerrado_sin_respuesta`.\n4. Si el miembro indica que no desea recibir mensajes → cerrar caso y marcar `opt_out`.\n\n**Tabla de clasificación de respuestas:**\n\n| Respuesta | Clasificación | Acción |\n|---|---|---|\n| \"Todo bien\", \"Gracias\", \"Genial\", \"Perfecto\" | `positiva` | Cerrar caso |\n| \"Bien\", \"OK\", \"Normal\" | `neutra` | Cerrar caso |\n| \"La máquina no funciona\", \"Estaba sucio\", \"El aire no enfría\" | `problema_instalaciones` | Escalar a humano |\n| \"El instructor fue grosero\", \"No me ayudaron\" | `problema_personal` | Escalar a humano |\n| \"Tengo una lesión\", \"Me lastimé\" | `problema_salud` | Escalar a humano |\n| \"No quiero mensajes\", \"Dejen de escribir\" | `opt_out` | Cerrar caso, marcar opt_out |\n| Sin respuesta en 48h | `sin_respuesta` | Recordatorio o cerrar |\n\n**Tabla de estados del caso:**\n\n| Estado | Condición |\n|---|---|\n| `pendiente_seguimiento` | Check-in detectado, esperando intervalo |\n| `seguimiento_enviado` | Mensaje enviado, esperando respuesta |\n| `respuesta_recibida` | Miembro respondió, procesando |\n| `cerrado` | Caso resuelto (respuesta positiva/neutra o sin respuesta tras 2 intentos) |\n| `escorado` | Caso transferido a humano |\n| `opt_out` | Miembro solicitó no recibir mensajes |\n\n---\n\n## Paso 6 — Escalamiento a humano (si aplica)\n\nSi la respuesta del miembro indica un problema, el agente escala el caso.\n\n**Acción del agente:**\n1. Generar el registro de escalamiento (ver `formato-salida.md` → sección 3).\n2. Enviar notificación al `responsable_seguimiento` por el canal configurado.\n3. Marcar el caso como `escorado`.\n4. Informar al miembro que un humano lo contactará.\n5. Registrar la marca de tiempo de escalamiento.\n\n---\n*Plantilla generada para el nicho Gimnasios y Fitness — Agente Check-in Follow-up Agent*\n"
              },
              {
                "id": "check-in-follow-up-agent/formato-salida.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "formato-salida.md",
                "contenido": "# Formato de salida — Check-in Follow-up Agent\n\nEste archivo define la estructura exacta de los mensajes y registros que el agente debe producir. No se permite desviarse de estas plantillas.\n\n---\n\n## 1. Mensaje de seguimiento estándar\n\nSe genera en el paso 3 del flujo. Se envía por el canal configurado (`canal_preferido`).\n\n### Plantilla — Seguimiento estándar (whatsapp/sms)\n\n```\n¡Hola {{nombre_miembro}}! Nos alegra que nos visitaste hoy 💪\n\n📅 Check-in: {{fecha_checkin_formateada}}\n⏱️ Duración: {{duracion_sesion}} minutos\n🏋️ Área: {{area_utilizada}}\n\n¿Cómo fue tu experiencia? Si necesitás algo o tenés feedback, escribinos.\n— {{gimnasio_nombre}}\n```\n\n### Plantilla — Seguimiento estándar (email)\n\n**Asunto:** ¡Gracias por visitarnos! — {{gimnasio_nombre}}\n\n```\nHola {{nombre_miembro}},\n\n¡Gracias por visitar {{gimnasio_nombre}} hoy! 💪\n\n📅 Check-in: {{fecha_checkin_formateada}}\n⏱️ Duración: {{duracion_sesion}} minutos\n🏋️ Área: {{area_utilizada}}\n\n¿Cómo fue tu experiencia? Si tenés feedback, sugerencias o necesitás algo, no dudes en escribirnos.\n\n¡Te esperamos la próxima vez!\n— El equipo de {{gimnasio_nombre}}\n```\n\n**Ejemplo renderizado (whatsapp):**\n\n```\n¡Hola Ana García López! Nos alegra que nos visitaste hoy 💪\n\n📅 Check-in: 23/08/2026 09:15\n⏱️ Duración: 75 minutos\n🏋️ Área: Sala de peso libre\n\n¿Cómo fue tu experiencia? Si necesitás algo o tenés feedback, escribinos.\n— FitZone Premium\n```\n\n---\n\n## 2. Mensaje de seguimiento — primera visita\n\n### Plantilla (whatsapp/sms)\n\n```\n¡Hola {{nombre_miembro}}! Bienvenido/a por primera vez a {{gimnasio_nombre}} 🎉\n\n📅 Tu primera visita: {{fecha_checkin_formateada}}\n⏱️ Duración: {{duracion_sesion}} minutos\n🏋️ Área: {{area_utilizada}}\n\n¿Qué tal tu primera experiencia? Si tenés preguntas sobre nuestras instalaciones, clases o servicios, escribinos.\n— {{gimnasio_nombre}}\n```\n\n**Ejemplo renderizado (whatsapp):**\n\n```\n¡Hola Carlos Ruiz! Bienvenido/a por primera vez a FitZone Premium 🎉\n\n📅 Tu primera visita: 23/08/2026 10:00\n⏱️ Duración: 45 minutos\n🏋️ Área: Cardio\n\n¿Qué tal tu primera experiencia? Si tenés preguntas sobre nuestras instalaciones, clases o servicios, escribinos.\n— FitZone Premium\n```\n\n---\n\n## 3. Mensaje de seguimiento — sesión larga\n\n### Plantilla (whatsapp/sms)\n\n```\n¡Hola {{nombre_miembro}}! 💪 Vemos que tuviste una sesión intensa hoy.\n\n📅 Check-in: {{fecha_checkin_formateada}}\n⏱️ Duración: {{duracion_sesion}} minutos\n🏋️ Área: {{area_utilizada}}\n\nRecordá hidratarte bien y hacer estiramientos. ¿Cómo te sentiste?\n— {{gimnasio_nombre}}\n```\n\n---\n\n## 4. Mensaje de seguimiento — asistencia irregular\n\n### Plantilla (whatsapp/sms)\n\n```\n¡Hola {{nombre_miembro}}! Te extrañamos 💪\n\nLlevás {{dias_sin_asistir}} días sin visitarnos. ¿Todo bien?\n\nSi necesitás ayuda con tu rutina o tenés alguna consulta, escribinos. ¡Te esperamos!\n— {{gimnasio_nombre}}\n```\n\n---\n\n## 5. Mensaje de recordatorio (segundo intento)\n\n### Plantilla (whatsapp/sms)\n\n```\n¡Hola {{nombre_miembro}}! 👋\n\n¿Cómo fue tu visita a {{gimnasio_nombre}}? Tu opinión nos ayuda a mejorar.\n\nSi ya nos respondiste, ignorá este mensaje.\n— {{gimnasio_nombre}}\n```\n\n---\n\n## 6. Mensaje de escalamiento al miembro\n\n### Plantilla (whatsapp/sms)\n\n```\nHola {{nombre_miembro}}, tu consulta requiere atención personalizada de nuestro equipo.\n\nUn miembro de {{gimnasio_nombre}} te contactará en las próximas horas para ayudarte.\n\nSi tenés alguna duda, escribinos por este canal.\n— {{gimnasio_nombre}}\n```\n\n---\n\n## 7. Registro de acción (JSON)\n\n**Estructura del registro:**\n\n```json\n{\n  \"registro_id\": \"FUP-REG-[timestamp]\",\n  \"agente\": \"check-in-follow-up-agent\",\n  \"followup_id\": \"[followup_id]\",\n  \"contacto_id\": \"[contacto_id]\",\n  \"nombre_miembro\": \"[nombre_miembro]\",\n  \"gimnasio_id\": \"[gimnasio_id]\",\n  \"gimnasio_nombre\": \"[gimnasio_nombre]\",\n  \"evento_tipo\": \"[evento_tipo]\",\n  \"fecha_hora_checkin\": \"[fecha_hora_checkin]\",\n  \"duracion_sesion_minutos\": \"[duracion_sesion_minutos]\",\n  \"area_utilizada\": \"[area_utilizada]\",\n  \"clase_asistida\": \"[clase_asistida]\",\n  \"canal_preferido\": \"[canal_preferido]\",\n  \"fecha_hora_envio_seguimiento\": \"[fecha_hora_envio_seguimiento]\",\n  \"respuesta_miembro\": \"[respuesta_miembro]\",\n  \"texto_respuesta\": \"[texto_respuesta]\",\n  \"fecha_hora_respuesta\": \"[fecha_hora_respuesta]\",\n  \"estado_caso\": \"[estado_caso]\",\n  \"resultado\": \"[resultado]\",\n  \"fecha_hora_cierre\": \"[fecha_hora_cierre]\",\n  \"timestamp_registro\": \"[tiempo_fecha_actual]\"\n}\n```\n\n**Ejemplo de registro completo (respuesta positiva):**\n\n```json\n{\n  \"registro_id\": \"FUP-REG-20260823120000\",\n  \"agente\": \"check-in-follow-up-agent\",\n  \"followup_id\": \"FUP-20260823-092000-001\",\n  \"contacto_id\": \"CON-20260115-101\",\n  \"nombre_miembro\": \"Ana García López\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"evento_tipo\": \"CHECKIN_REGISTRADO\",\n  \"fecha_hora_checkin\": \"2026-08-23T09:15:00\",\n  \"duracion_sesion_minutos\": 75,\n  \"area_utilizada\": \"Sala de peso libre\",\n  \"clase_asistida\": null,\n  \"canal_preferido\": \"whatsapp\",\n  \"fecha_hora_envio_seguimiento\": \"2026-08-23T11:15:00\",\n  \"respuesta_miembro\": \"positiva\",\n  \"texto_respuesta\": \"Todo excelente, gracias!\",\n  \"fecha_hora_respuesta\": \"2026-08-23T12:00:00\",\n  \"estado_caso\": \"cerrado\",\n  \"resultado\": \"respuesta_positiva\",\n  \"fecha_hora_cierre\": \"2026-08-23T12:00:00\",\n  \"timestamp_registro\": \"2026-08-23T12:00:00\"\n}\n```\n\n**Ejemplo de registro completo (problema reportado):**\n\n```json\n{\n  \"registro_id\": \"FUP-REG-20260823140000\",\n  \"agente\": \"check-in-follow-up-agent\",\n  \"followup_id\": \"FUP-20260823-100000-002\",\n  \"contacto_id\": \"CON-20260201-205\",\n  \"nombre_miembro\": \"Roberto Sánchez\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"evento_tipo\": \"CHECKIN_REGISTRADO\",\n  \"fecha_hora_checkin\": \"2026-08-23T10:00:00\",\n  \"duracion_sesion_minutos\": 60,\n  \"area_utilizada\": \"Sala de cardio\",\n  \"clase_asistida\": null,\n  \"canal_preferido\": \"whatsapp\",\n  \"fecha_hora_envio_seguimiento\": \"2026-08-23T12:00:00\",\n  \"respuesta_miembro\": \"problema_instalaciones\",\n  \"texto_respuesta\": \"La cinta de correr #3 no funciona bien, hace un ruido raro\",\n  \"fecha_hora_respuesta\": \"2026-08-23T14:00:00\",\n  \"estado_caso\": \"escorado\",\n  \"resultado\": \"escalado_problema_instalaciones\",\n  \"fecha_hora_cierre\": \"2026-08-23T14:00:00\",\n  \"timestamp_registro\": \"2026-08-23T14:00:00\"\n}\n```\n\n---\n\n## 8. Registro de escalamiento\n\n```json\n{\n  \"escalamiento_id\": \"FUP-ESC-[timestamp]\",\n  \"agente\": \"check-in-follow-up-agent\",\n  \"gimnasio_id\": \"[gimnasio_id]\",\n  \"gimnasio_nombre\": \"[gimnasio_nombre]\",\n  \"contacto_id\": \"[contacto_id]\",\n  \"nombre_miembro\": \"[nombre_miembro]\",\n  \"followup_id\": \"[followup_id]\",\n  \"motivo_escalamiento\": \"[motivo_enum]\",\n  \"descripcion_caso\": \"[texto_descriptivo]\",\n  \"urgencia\": \"[alta | media | baja]\",\n  \"asignado_a\": \"[nombre_humano_asignado]\",\n  \"timestamp_escalamiento\": \"[tiempo_fecha_actual]\",\n  \"datos_contexto\": {\n    \"evento_tipo\": \"[evento_tipo]\",\n    \"fecha_hora_checkin\": \"[fecha_hora_checkin]\",\n    \"area_utilizada\": \"[area_utilizada]\",\n    \"texto_respuesta\": \"[texto_respuesta]\"\n  }\n}\n```\n\n**Ejemplo de registro de escalamiento:**\n\n```json\n{\n  \"escalamiento_id\": \"FUP-ESC-20260823140100\",\n  \"agente\": \"check-in-follow-up-agent\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"contacto_id\": \"CON-20260201-205\",\n  \"nombre_miembro\": \"Roberto Sánchez\",\n  \"followup_id\": \"FUP-20260823-100000-002\",\n  \"motivo_escalamiento\": \"problema_instalaciones\",\n  \"descripcion_caso\": \"El miembro Roberto Sánchez reportó que la cinta de correr #3 no funciona bien y hace un ruido raro. Requiere revisión del equipo técnico.\",\n  \"urgencia\": \"media\",\n  \"asignado_a\": \"Recepción\",\n  \"timestamp_escalamiento\": \"2026-08-23T14:01:00\",\n  \"datos_contexto\": {\n    \"evento_tipo\": \"CHECKIN_REGISTRADO\",\n    \"fecha_hora_checkin\": \"2026-08-23T10:00:00\",\n    \"area_utilizada\": \"Sala de cardio\",\n    \"texto_respuesta\": \"La cinta de correr #3 no funciona bien, hace un ruido raro\"\n  }\n}\n```\n\n---\n*Plantilla generada para el nicho Gimnasios y Fitness — Agente Check-in Follow-up Agent*\n"
              },
              {
                "id": "check-in-follow-up-agent/prompt-sistema.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "prompt-sistema.md",
                "contenido": "# Prompt del sistema — Check-in Follow-up Agent\n\n## Identidad\nEres **FitBot**, asistente virtual de **GymPro Fitness Center**. Tu función es dar seguimiento automático después de cada check-in del miembro en el gimnasio, enviando mensajes personalizados que refuercen el hábito y recopilen feedback sobre la experiencia.\n\n## Tono de comunicación\n- **Cercano y personal**: el mensaje se siente como un follow-up genuino, no como un mensaje automático genérico.\n- **Motivador**: reconocer la asistencia del miembro y reforzar el hábito positivo.\n- **Orientado a la experiencia**: preguntar cómo se sintió, si todo estuvo bien, si necesita algo.\n- **Breve pero completo**: máximo 3 párrafos por mensaje. Directo al punto.\n- **Sin presionar**: máximo 2 mensajes de seguimiento por check-in. Si no hay respuesta, se cierra el caso.\n\n## Flujo de ejecución\nEjecuta los siguientes pasos en orden exacto, sin omitir ni reordenar:\n\n### Paso 1 — Detección del evento disparador (check-in registrado)\nDetecta que un miembro completó su check-in en recepción o a través de la app del gimnasio.\n\n### Paso 2 — Espera del intervalo de tiempo configurado\nEspera el intervalo de tiempo configurado antes de enviar el mensaje de seguimiento.\n\n### Paso 3 — Redacción del mensaje de seguimiento personalizado\nGenera el mensaje de seguimiento personalizado con los datos del check-in del miembro.\n\n### Paso 4 — Envío del mensaje por el canal configurado\nEnvía el mensaje de seguimiento al miembro por el canal preferido.\n\n### Paso 5 — Registro de la respuesta del miembro y cierre del caso\nEspera la respuesta del miembro y la registra en el historial.\n\n### Paso 6 — Marca el caso como cerrado o lo escala a un humano según la respuesta recibida\nCierra el caso o transfiere a humano dependiendo de la clasificación de la respuesta.\n\n## Ejemplo de ejecución\n\n**Entrada:**\n- nombre_miembro: \"Ana García López\"\n- fecha_checkin: \"2026-08-23 09:15\"\n- duracion_sesion: 75 minutos\n- area_utilizada: \"Sala de peso libre\"\n\n**Salida generada (paso 3 — mensaje de seguimiento):**\n```\n¡Hola Ana García! Nos alegra que nos visitaste hoy 💪\n\n📅 Check-in: 23/08/2026 09:15\n⏱️ Duración: 75 minutos\n🏋️ Área: Sala de peso libre\n\n¿Cómo fue tu experiencia? Si necesitás algo o tenés feedback, escribinos.\n```\n\n## Eventos que activan el flujo\n\n| Código | Evento | Intervalo de espera |\n|---|---|---|\n| `CHECKIN_REGISTRADO` | Check-in completado | 2 horas después del check-in |\n| `PRIMERA_VISITA` | Primera vez que asiste | 1 hora después del check-in |\n| `SESION_LARGA` | Sesión prolongada (> 120 min) | 1 hora después del check-in |\n| `SESION_CORTA` | Sesión muy corta (< 15 min) | 2 horas después del check-in |\n| `ASISTENCIA_IRREGULAR` | Asistencia irregular (> 7 días sin asistir) | Inmediatamente al registrar |\n\n## Restricciones\n- No enviar mensajes fuera del horario de atención (antes de las 07:00 o después de las 22:00).\n- No compartir información de otros miembros. Cada caso es individual.\n- Máximo 2 mensajes por check-in.\n- No inventar datos. Si algún campo no está disponible, usar \"N/D\" y continuar.\n\n## Reglas de escalamiento\n- Si el miembro reporta un problema con las instalaciones → escalar a humano.\n- Si el miembro reporta un problema con el personal → escalar a humano.\n- Si el miembro indica que tiene una lesión o se lastimó → escalar a humano.\n- Si el envío del mensaje falla 2 veces consecutivas → escalar a humano.\n- Tiempo máximo de respuesta: 2 minutos. Si excede, escalar a equipo humano.\n"
              },
              {
                "id": "check-in-follow-up-agent/reglas-escalamiento.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "reglas-escalamiento.md",
                "contenido": "# Reglas de escalamiento — Check-in Follow-up Agent\n\nEste archivo define la lista cerrada y exacta de condiciones bajo las cuales el agente **debe** notificar al responsable humano (recepcionista, encargado o supervisor). No existe discrecionalidad: si se cumple alguna condición, la notificación es **obligatoria**.\n\n---\n\n## Condiciones de notificación\n\n### C1 — Problemas con las instalaciones o equipamiento\n\n| # | Condición | Acción | Plazo |\n|---|---|---|---|\n| C1.1 | El miembro reporta que un equipo no funciona (contiene palabras clave: \"no funciona\", \"roto\", \"dañado\", \"ruido\", \"no enciende\") | Notificar al responsable de mantenimiento. Motivo: `Equipo reportado como defectuoso: {{texto_respuesta}}. Área: {{area_utilizada}}.` Plazo: **4 horas**. | 4 horas |\n| C1.2 | El miembro reporta que las instalaciones están sucias o en mal estado | Notificar al encargado. Motivo: `Reporte de limpieza/estado de instalaciones: {{texto_respuesta}}.` Plazo: **4 horas**. | 4 horas |\n| C1.3 | El miembro reporta que falta material (pesas, bandas, toallas, etc.) | Notificar al encargado. Motivo: `Material faltante reportado: {{texto_respuesta}}. Área: {{area_utilizada}}.` Plazo: **24 horas**. | 24 horas |\n\n### C2 — Problemas con el personal\n\n| # | Condición | Acción | Plazo |\n|---|---|---|---|\n| C2.1 | El miembro reporta mal trato del personal (contiene palabras clave: \"grosero\", \"mal trato\", \"insulto\", \"desprecio\") | Notificar al gerente inmediatamente. Motivo: `Reporte de mal trato del personal: {{texto_respuesta}}.` Plazo: **2 horas**. | 2 horas |\n| C2.2 | El miembro reporta que no recibió ayuda cuando la solicitó | Notificar al encargado. Motivo: `Miembro reporta falta de asistencia: {{texto_respuesta}}.` Plazo: **4 horas**. | 4 horas |\n\n### C3 — Problemas de salud o seguridad\n\n| # | Condición | Acción | Plazo |\n|---|---|---|---|\n| C3.1 | El miembro indica que se lastimó o tiene una lesión durante la visita (contiene palabras clave: \"lastimé\", \"lesión\", \"dolor\", \"me caí\", \"accidente\") | Notificar al gerente + contacts de emergencia si aplica. Motivo: `Miembro reportó lesión o incidente: {{texto_respuesta}}.` Plazo: **1 hora**. | 1 hora |\n| C3.2 | El miembro reporta que el gimnasio no es seguro (contiene palabras clave: \"peligro\", \"inseguro\", \"miedo\") | Notificar al gerente inmediatamente. Motivo: `Reporte de inseguridad: {{texto_respuesta}}.` Plazo: **1 hora**. | 1 hora |\n\n### C4 — Primera visita con problemas\n\n| # | Condición | Acción | Plazo |\n|---|---|---|---|\n| C4.1 | Primera visita + respuesta negativa o de problema | Notificar al encargado. Motivo: `Nuevo miembro reportó problema en su primera visita: {{texto_respuesta}}. Riesgo de pérdida del cliente.` Plazo: **2 horas**. | 2 horas |\n| C4.2 | Primera visita + duración < 10 minutos (posible salida temprana) | Notificar al encargado. Motivo: `Nuevo miembro se fue después de {{duracion_sesion_minutos}} minutos. Posible insatisfacción con la experiencia inicial.` Plazo: **4 horas**. | 4 horas |\n\n### C5 — Satisfacción muy baja\n\n| # | Condición | Acción | Plazo |\n|---|---|---|---|\n| C5.1 | El miembro expresa insatisfacción extrema (contiene palabras clave: \"terrible\", \"pésimo\", \"nunca más\", \"devolver dinero\") | Notificar al gerente. Motivo: `Miembro reporta insatisfacción extrema: {{texto_respuesta}}. Riesgo de churn inmediato.` Plazo: **2 horas**. | 2 horas |\n\n### C6 — Solicitud de cancelación o baja\n\n| # | Condición | Acción | Plazo |\n|---|---|---|---|\n| C6.1 | El miembro indica que quiere cancelar su membresía o darse de baja | Notificar al responsable de retención. Motivo: `Miembro expresa intención de cancelación: {{texto_respuesta}}.` Plazo: **2 horas**. | 2 horas |\n| C6.2 | El miembro solicita reembolso o devolución de dinero | Notificar al gerente. Motivo: `Miembro solicita reembolso: {{texto_respuesta}}.` Plazo: **4 horas**. | 4 horas |\n\n### C7 — Fallo técnico del sistema\n\n| # | Condición | Acción | Plazo |\n|---|---|---|---|\n| C7.1 | El envío del mensaje de seguimiento falla dos veces consecutivas | Registrar el error. Notificar al equipo técnico. El caso queda pendiente. | 24 horas |\n| C7.2 | El canal de comunicación del miembro no está configurado o es inválido | Notificar al equipo de datos para verificar el registro. Usar `email` como respaldo si está disponible. | 24 horas |\n\n---\n\n## Reglas de asignación de urgencia\n\n| Nivel | Color | Significado | Tiempo máximo de espera |\n|-------|-------|-------------|------------------------|\n| **Alta** | 🔴 | Requiere intervención inmediata (lesión, mal trato, inseguridad) | 1-2 horas |\n| **Media** | 🟡 | Requiere revisión pronta (equipamiento, primera visita, insatisfacción) | 4 horas |\n| **Baja** | 🟢 | Informativo, requiere seguimiento pero no es urgente | 24 horas |\n\n---\n\n## Reglas de notificación por urgencia\n\n| Urgencia | Canal de notificación | Destinatario |\n|----------|----------------------|--------------|\n| **Alta** | WhatsApp + Email | Responsable + Supervisor/Gerente |\n| **Media** | Email | Responsable |\n| **Baja** | Registro en sistema | Encargado |\n\n---\n\n## Textos base para escalamiento\n\n### Al miembro (cuando se escala):\n\n```\nHola {{nombre_miembro}}, tu consulta requiere atención personalizada de nuestro equipo.\n\nUn miembro de {{gimnasio_nombre}} te contactará en las próximas horas para ayudarte.\n\nSi tenés alguna duda, escribinos por este canal.\n— {{gimnasio_nombre}}\n```\n\n### Al equipo interno (notificación de escalamiento):\n\n```\n🚨 ESCALAMIENTO — Check-in Follow-up Agent\n\n📋 Caso: {{escalamiento_id}}\n👤 Miembro: {{nombre_miembro}} ({{contacto_id}})\n📅 Check-in: {{fecha_hora_checkin}}\n🏋️ Área: {{area_utilizada}}\n\n⚠️ Motivo: {{motivo_escalamiento}}\n📝 Detalle: {{descripcion_caso}}\n🔴 Urgencia: {{urgencia}}\n⏰ Plazo de respuesta: {{plazo}} horas\n👤 Asignado a: {{asignado_a}}\n```\n\n**Ejemplo de notificación al equipo:**\n\n```\n🚨 ESCALAMIENTO — Check-in Follow-up Agent\n\n📋 Caso: FUP-ESC-20260823140100\n👤 Miembro: Roberto Sánchez (CON-20260201-205)\n📅 Check-in: 23/08/2026 10:00\n🏋️ Área: Sala de cardio\n\n⚠️ Motivo: problema_instalaciones\n📝 Detalle: El miembro Roberto Sánchez reportó que la cinta de correr #3 no funciona bien y hace un ruido raro. Requiere revisión del equipo técnico.\n🔴 Urgencia: media\n⏰ Plazo de respuesta: 4 horas\n👤 Asignado a: Recepción\n```\n\n---\n\n## Ejemplo completo de escalamiento\n\n**Caso:** Roberto Sánchez reporta que la cinta de correr #3 no funciona.\n\n**Registro de escalamiento generado:**\n\n```json\n{\n  \"escalamiento_id\": \"FUP-ESC-20260823140100\",\n  \"agente\": \"check-in-follow-up-agent\",\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"contacto_id\": \"CON-20260201-205\",\n  \"nombre_miembro\": \"Roberto Sánchez\",\n  \"followup_id\": \"FUP-20260823-100000-002\",\n  \"motivo_escalamiento\": \"problema_instalaciones\",\n  \"descripcion_caso\": \"El miembro Roberto Sánchez reportó que la cinta de correr #3 no funciona bien y hace un ruido raro. Requiere revisión del equipo técnico.\",\n  \"urgencia\": \"media\",\n  \"asignado_a\": \"Recepción\",\n  \"timestamp_escalamiento\": \"2026-08-23T14:01:00\",\n  \"datos_contexto\": {\n    \"evento_tipo\": \"CHECKIN_REGISTRADO\",\n    \"fecha_hora_checkin\": \"2026-08-23T10:00:00\",\n    \"area_utilizada\": \"Sala de cardio\",\n    \"texto_respuesta\": \"La cinta de correr #3 no funciona bien, hace un ruido raro\"\n  }\n}\n```\n\n**Acciones tomadas:**\n1. Se envió mensaje a Roberto indicando que un humano lo contactará.\n2. Se notificó a Recepción por email.\n3. El caso quedó marcado como `escorado` hasta recibir instrucción.\n\n---\n*Plantilla generada para el nicho Gimnasios y Fitness — Agente Check-in Follow-up Agent*\n"
              },
              {
                "id": "check-in-follow-up-agent/variables-entrada.md",
                "etiqueta": "",
                "es_plantilla": true,
                "nombre": "variables-entrada.md",
                "contenido": "# Variables de entrada — Check-in Follow-up Agent\n\nEste archivo define la lista fija y cerrada de campos de datos que el agente necesita recibir para operar. No se admiten campos adicionales a los listados aquí.\n\n---\n\n## Campos del evento disparador\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 1 | `evento_id` | `string` | Obligatorio | Identificador único del evento. Formato: `CHK-YYYYMMDD-HHMMSS-XXX`. | `\"CHK-20260823-091500-001\"` |\n| 2 | `contacto_id` | `string` | Obligatorio | Identificador del contacto. Formato: `CON-YYYYMMDD-XXX`. | `\"CON-20260115-101\"` |\n| 3 | `evento_tipo` | `string` (enum) | Obligatorio | Tipo de evento. Valores: `CHECKIN_REGISTRADO`, `PRIMERA_VISITA`, `SESION_LARGA`, `SESION_CORTA`, `ASISTENCIA_IRREGULAR`. | `\"CHECKIN_REGISTRADO\"` |\n| 4 | `fecha_hora_checkin` | `string` (ISO 8601) | Obligatorio | Fecha y hora del check-in. | `\"2026-08-23T09:15:00\"` |\n\n---\n\n## Datos del miembro\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 5 | `nombre_miembro` | `string` | Obligatorio | Nombre completo del miembro. | `\"Ana García López\"` |\n| 6 | `telefono_miembro` | `string` | Obligatorio | Número telefónico del miembro. Entre 8 y 15 dígitos. | `\"+52 55 9876 5432\"` |\n| 7 | `email_miembro` | `string` | Opcional (requerido si `canal_preferido` = `email`) | Correo electrónico del miembro. | `\"ana.garcia@correo.com\"` |\n| 8 | `canal_preferido` | `string` (enum) | Obligatorio | Canal preferido para comunicación. Valores: `whatsapp`, `sms`, `email`. | `\"whatsapp\"` |\n| 9 | `estado_cliente` | `string` (enum) | Obligatorio | Estado del cliente. Solo se procesan clientes con estado `activo`. Valores: `activo`, `suspendido`, `baja`. | `\"activo\"` |\n\n---\n\n## Datos del check-in\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 10 | `duracion_sesion_minutos` | `integer` | Obligatorio | Duración de la sesión en minutos. | `75` |\n| 11 | `area_utilizada` | `string` | Obligatorio | Área o zona del gimnasio utilizada. | `\"Sala de peso libre\"` |\n| 12 | `clase_asistida` | `string` o `null` | Opcional | Nombre de la clase asistida (si fue a una clase grupal). `null` si fue entrenamiento libre. | `null` |\n| 13 | `instructor_nombre` | `string` o `null` | Opcional | Nombre del instructor de la clase (si aplica). `null` si fue entrenamiento libre. | `null` |\n\n---\n\n## Datos de historial de asistencia\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 14 | `checkins_previos_mes` | `integer` | Obligatorio | Número de check-ins del miembro en el mes actual. | `8` |\n| 15 | `dias_desde_ultimo_checkin` | `integer` | Obligatorio | Días transcurridos desde el último check-in. | `2` |\n| 16 | `primera_vez` | `boolean` | Obligatorio | `true` si es la primera vez que el miembro hace check-in. | `false` |\n\n---\n\n## Datos del gimnasio\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 17 | `gimnasio_id` | `string` | Obligatorio | Identificador único del gimnasio. | `\"GYM-001\"` |\n| 18 | `gimnasio_nombre` | `string` | Obligatorio | Nombre comercial del gimnasio. | `\"FitZone Premium\"` |\n| 19 | `gimnasio_telefono` | `string` | Opcional | Teléfono del gimnasio. | `\"+52 55 1234 5678\"` |\n\n---\n\n## Datos de notificación\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 20 | `responsable_seguimiento` | `string` | Obligatorio | Nombre del responsable de seguimiento. | `\"Recepción\"` |\n| 21 | `email_responsable` | `string` | Opcional | Correo electrónico del responsable. | `\"recepcion@fitzone.com\"` |\n\n---\n\n## Datos de tiempos\n\n| # | Nombre del campo | Tipo de dato | Obligatorio / Opcional | Descripción | Ejemplo |\n|---|---|---|---|---|---|\n| 22 | `tiempo_fecha_actual` | `string` (ISO 8601) | Obligatorio | Fecha y hora actual del sistema. | `\"2026-08-23T09:20:00\"` |\n| 23 | `horario_atencion_inicio` | `string` (HH:MM) | Opcional | Hora de inicio del horario de atención. Default: `\"07:00\"`. | `\"07:00\"` |\n| 24 | `horario_atencion_fin` | `string` (HH:MM) | Opcional | Hora de fin del horario de atención. Default: `\"22:00\"`. | `\"22:00\"` |\n\n---\n\n## Campos calculados por el agente\n\nEstos campos **no se reciben de entrada**; el agente los genera internamente.\n\n| # | Nombre del campo | Tipo de dato | Generado en | Descripción |\n|---|---|---|---|---|\n| 25 | `followup_id` | `string` | Paso 1 | ID único del seguimiento. Formato: `FUP-YYYYMMDD-HHMMSS-XXX`. |\n| 26 | `fecha_hora_deteccion_checkin` | `string` (ISO 8601) | Paso 1 | Marca de tiempo de detección del check-in. |\n| 27 | `intervalo_horas` | `integer` | Paso 1 | Horas de espera configuradas según tipo de evento. |\n| 28 | `momento_envio_seguimiento_calculado` | `string` (ISO 8601) | Paso 2 | Momento de envío calculado. |\n| 29 | `momento_envio_seguimiento_ajustado` | `string` (ISO 8601) | Paso 2 | Momento de envío ajustado al horario. |\n| 30 | `ajuste_horario` | `boolean` | Paso 2 | `true` si se ajustó el horario. |\n| 31 | `fecha_hora_envio_seguimiento` | `string` (ISO 8601) o `null` | Paso 4 | Marca de envío del seguimiento. |\n| 32 | `respuesta_miembro` | `string` (enum) o `null` | Paso 5 | Clasificación de la respuesta. |\n| 33 | `texto_respuesta` | `string` o `null` | Paso 5 | Texto exacto de la respuesta. |\n| 34 | `fecha_hora_respuesta` | `string` (ISO 8601) o `null` | Paso 5 | Marca de la respuesta. |\n| 35 | `estado_caso` | `string` (enum) | Paso 5 | Estado actual del caso. |\n| 36 | `resultado` | `string` (enum) | Paso 5 | Resultado final. |\n| 37 | `fecha_hora_cierre` | `string` (ISO 8601) | Paso 5 | Marca de cierre del caso. |\n\n---\n\n## Validaciones de entrada\n\n| Campo | Regla | Acción si falla |\n|---|---|---|\n| `evento_id` | No vacío, formato válido | Terminar flujo sin ejecutar |\n| `contacto_id` | Debe seguir formato `CON-YYYYMMDD-XXX` | Terminar flujo sin ejecutar |\n| `evento_tipo` | Debe ser uno de los 5 valores válidos | Terminar flujo sin ejecutar |\n| `nombre_miembro` | No vacío, mínimo 2 caracteres | Terminar flujo sin ejecutar |\n| `telefono_miembro` | Entre 8 y 15 dígitos si `canal_preferido` es `whatsapp` o `sms` | Escalar a humano |\n| `email_miembro` | Requerido si `canal_preferido` es `email` | Escalar a humano |\n| `estado_cliente` | Debe ser `activo` | No procesar seguimiento |\n| `duracion_sesion_minutos` | Número positivo > 0 | Terminar flujo sin ejecutar |\n| `area_utilizada` | No vacío | Usar `\"No especificada\"` |\n| `gimnasio_id` | No vacío | Terminar flujo sin ejecutar |\n\n---\n\n## Ejemplo de payload completo\n\n```json\n{\n  \"evento_id\": \"CHK-20260823-091500-001\",\n  \"contacto_id\": \"CON-20260115-101\",\n  \"evento_tipo\": \"CHECKIN_REGISTRADO\",\n  \"fecha_hora_checkin\": \"2026-08-23T09:15:00\",\n  \"nombre_miembro\": \"Ana García López\",\n  \"telefono_miembro\": \"+52 55 9876 5432\",\n  \"email_miembro\": \"ana.garcia@correo.com\",\n  \"canal_preferido\": \"whatsapp\",\n  \"estado_cliente\": \"activo\",\n  \"duracion_sesion_minutos\": 75,\n  \"area_utilizada\": \"Sala de peso libre\",\n  \"clase_asistida\": null,\n  \"instructor_nombre\": null,\n  \"checkins_previos_mes\": 8,\n  \"dias_desde_ultimo_checkin\": 2,\n  \"primera_vez\": false,\n  \"gimnasio_id\": \"GYM-001\",\n  \"gimnasio_nombre\": \"FitZone Premium\",\n  \"gimnasio_telefono\": \"+52 55 1234 5678\",\n  \"responsable_seguimiento\": \"Recepción\",\n  \"email_responsable\": \"recepcion@fitzone.com\",\n  \"tiempo_fecha_actual\": \"2026-08-23T09:20:00\",\n  \"horario_atencion_inicio\": \"07:00\",\n  \"horario_atencion_fin\": \"22:00\"\n}\n```\n\n---\n*Plantilla generada para el nicho Gimnasios y Fitness — Agente Check-in Follow-up Agent*\n"
              }
            ]
          }
        ]
      }
    ],
    "conjuntos": [
      {
        "id": "basico",
        "nombre": "🌟 Básico",
        "descripcion": "Agentes esenciales para cualquier gimnasio",
        "modulos": [
          "membership-enquiry",
          "trial-booking",
          "class-reminder",
          "membership-renewal"
        ]
      },
      {
        "id": "retencion",
        "nombre": "🔄 Retención",
        "descripcion": "Agentes para retener miembros y reducir cancelaciones",
        "modulos": [
          "churn-risk",
          "check-in-follow-up",
          "onboarding-journey",
          "goal-check-in"
        ]
      },
      {
        "id": "ventas",
        "nombre": "💰 Ventas",
        "descripcion": "Agentes para captar leads y vender membresías",
        "modulos": [
          "lead-nurture",
          "failed-payment",
          "referral-request"
        ]
      },
      {
        "id": "servicios",
        "nombre": "💪 Servicios",
        "descripcion": "Agentes para entrenamiento personal y nutrición",
        "modulos": [
          "personal-training-booking",
          "nutrition-plan-faq"
        ]
      },
      {
        "id": "comunidad",
        "nombre": "👥 Comunidad",
        "descripcion": "Agentes para engagement y comunidad",
        "modulos": [
          "challenge-promotion",
          "review-request",
          "attendance-recap",
          "waitlist-class-filler"
        ]
      },
      {
        "id": "completo",
        "nombre": "🚀 Completo",
        "descripcion": "Todos los agentes para un gimnasio completo",
        "modulos": [
          "membership-enquiry",
          "trial-booking",
          "class-reminder",
          "membership-renewal",
          "lead-nurture",
          "churn-risk",
          "check-in-follow-up",
          "personal-training-booking",
          "goal-check-in",
          "nutrition-plan-faq",
          "waitlist-class-filler",
          "review-request",
          "attendance-recap",
          "onboarding-journey",
          "failed-payment",
          "challenge-promotion",
          "referral-request"
        ]
      },
      {
        "id": "urgencia",
        "nombre": "🚨 Urgencia",
        "descripcion": "Agentes para cancelaciones y pagos fallidos",
        "modulos": [
          "churn-risk",
          "failed-payment",
          "waitlist-class-filler"
        ]
      },
      {
        "id": "horario-laboral",
        "nombre": "🕐 Horario laboral",
        "descripcion": "Agentes que funcionan en horario del gimnasio",
        "modulos": [
          "trial-booking",
          "class-reminder",
          "membership-renewal"
        ]
      }
    ],
    "dashboard": {
      "visibles": [
        "hero",
        "hoy",
        "resumen"
      ]
    }
  }
}