Trazabilidad

Cómo tratamos tus datos

Explicamos paso a paso el trayecto de tus datos dentro de Viral. Cuando la fase corresponde a lógica ejecutable, indicamos el fichero fuente donde vive la implementación y el nombre del verificador automático que la comprueba.

Quién es responsable de esta tecnología

Viral es propiedad de ApisDom Intelligence Group y está operado por ApisDom. El motor matemático de predicción es Chronos 2, un modelo de código abierto desarrollado por Amazon Science; ApisDom lo integra en su infraestructura y aporta toda la capa de servicio (autenticación, créditos, exportación, este sitio y las garantías descritas más abajo). En la página Cómo funciona, sección Tecnología, se detallan las capacidades del motor, sus límites y su rendimiento. Esta página añade lo que allí no cabe: el trayecto interno del dato dentro de Viral.

Cómo leer esta página

Los siete puntos siguientes describen las fases del tratamiento. Los cinco primeros corresponden a lógica ejecutable de Viral e incluyen dos referencias: el fichero fuente donde vive la implementación y el nombre del verificador automático que se ejecuta con npm run verify:all. El paso 6 indica la constante única del plazo y los ficheros que la usan; es configuración, no lógica algorítmica, y por eso no lleva verificador propio. El paso 7 recoge principios de diseño y políticas del servicio, no código compilable.

1. Qué columnas leemos de tu archivo

De tu CSV, XLSX o XLS solo leemos lo estrictamente necesario: la columna de fecha, la columna de valor y, opcionalmente, una tercera columna que indica si tu negocio estaba abierto ese día. Cualquier otra columna del archivo se ignora silenciosamente; si detectamos columnas extra, te lo advertimos en la propia pantalla de subida.

Reglas exactas:

  • Cabeceras que reconocemos para la tercera columna (mayúsculas, minúsculas y acentos indiferentes): is_open, is open, isopen, open, abierto, abierta, estado, status.
  • Valores que interpretamos como abierto: true, 1, sí, si, yes, y, abierto, abierta, open, opened, activo, active.
  • Valores que interpretamos como cerrado: false, 0, no, n, cerrado, cerrada, closed, close, inactivo, inactive.
  • Celda vacía se interpreta como cerrado. Cualquier otro texto no reconocido se interpreta como abierto por criterio conservador (preferimos no descartar un día ambiguo por un error de tipeo).
Implementación: src/lib/file-parser/is-open.ts
Verificador automático: audit:is-open (12 casos deterministas) y audit:pipeline (12 ficheros de referencia con cabeceras en distintos idiomas y typos comunes)

2. Cómo limpiamos los textos que escribes

Los textos libres que envías desde el navegador (nombre, mensaje de soporte, motivo de baja del newsletter y similares) pasan por una función central de saneado antes de guardarse en base de datos, incluirse en una descarga o enviarse por email. Es una capa invisible para el usuario cuyo objetivo es reducir el riesgo de que un texto llegue a un canal posterior sin normalizar.

Reglas exactas:

  • Eliminamos caracteres de control no imprimibles: rangos U+0000 a U+0008, U+000B, U+000C, U+000E a U+001F y U+007F. Los saltos de línea, tabuladores y retornos legítimos se preservan para el siguiente paso.
  • Colapsamos secuencias de whitespace múltiple en un único espacio y aplicamos trim al inicio y al final.
  • Truncamos cada campo a la longitud máxima definida por su esquema Zod (por ejemplo 100 caracteres para un nombre, 2.000 para un mensaje).
  • NO eliminamos ni escapamos HTML en este paso. React ya escapa por defecto al renderizar; el escape específico se aplica en el canal de uso (por ejemplo al inyectar en un email o al generar un PDF con jsPDF).
Implementación: src/lib/sanitize/text-input.ts
Verificador automático: audit:sanitize (16 casos deterministas incluidos BOM, emojis, HTML embebido, saltos de línea legítimos y strings de 10.000 caracteres)

3. Cómo detectamos si tu negocio cierra algunos días

Si tu archivo trae la columna de abierto/cerrado, la usamos tal cual, sin transformar sus valores. Si no la trae pero detectamos un patrón claro (por ejemplo todos los domingos aparecen a cero durante varias semanas) lo indicamos con un aviso en la pantalla de subida, para que puedas marcar esos días si tu negocio cierra ese día. No modificamos ni añadimos esa información por ti y la subida sigue adelante en cualquier caso.

Reglas exactas:

  • Cierre semanal (código weekly_closure): un mismo día de la semana está en cero en al menos el 80% de sus apariciones dentro del histórico, con un mínimo de 3 ocurrencias.
  • Racha larga (código long_streak): existe al menos una secuencia de 3 o más días consecutivos con valor cero.
  • Ratio alto (código high_zero_ratio): más del 20% del histórico son ceros, sin que se cumpla ninguno de los patrones anteriores.
  • Esta detección heurística SOLO se ejecuta cuando el usuario NO ha aportado la columna. Si el usuario ya nos ha dicho qué días estaban cerrados, respetamos su decisión sin sobreescribirla ni cuestionarla.
Implementación: src/lib/file-parser/zero-pattern.ts
Verificador automático: audit:zero-pattern (8 casos deterministas: los tres patrones anteriores y sus complementarios negativos, incluido el caso en que el usuario ya aportó is_open)

4. Cómo llegan tus datos al motor de predicción

Al motor Chronos 2 le enviamos únicamente lo que necesita para tu predicción: fechas, valores, número de días a predecir y, si nos aportaste la lista de días abiertos, la propagamos siguiendo cuatro reglas estrictas. Ningún otro dato tuyo (ni tu email, ni tu IP, ni tu histórico previo) llega al motor.

Reglas exactas:

  • Regla 1: si no tienes columna de abierto/cerrado, el campo no se envía. El motor la trata como opcional y funciona igual que sin ella.
  • Regla 2: si su longitud no coincide con el número de fechas, el campo no se envía. Es una defensa en profundidad para evitar un rechazo del motor y proteger tu petición.
  • Regla 3: si todos los valores son abierto, el campo no se envía. Equivale a no mandarlo y ahorra ancho de banda.
  • Regla 4: si contiene al menos un día cerrado, se envía tal cual. El motor sustituye ese día por dato ausente (NaN) y no lo aprende como una venta real de cero euros.
  • Estas cuatro reglas están respaldadas por una regla estática adicional en el verificador de errores (MISSING_IS_OPEN_PROPAGATION), que exige que el adapter siga referenciando input.is_open. Si esa referencia desaparece, la regla marca error al ejecutar verify:errors.
Implementación: src/adapters/prediction/ApisdomAdapter.ts (método buildPayload)
Verificador automático: audit:is-open-adapter (6 casos que cubren las 4 reglas y sus edge cases) más la regla estática MISSING_IS_OPEN_PROPAGATION en verify:errors

5. Cómo comparamos cifras en las tarjetas de porcentaje

Las tarjetas que ves con porcentajes (por ejemplo tus ventas subirán un 12%) comparan la suma de los días predichos contra la suma del mismo número de días reales anteriores. Si nos aportaste los días de cierre, los excluimos de esa comparación para que el porcentaje no salga sesgado a la baja o al alza.

Reglas exactas:

  • Denominador (baseline): suma de los últimos N valores del histórico, siendo N el número de días que has pedido predecir.
  • Si la columna de abierto/cerrado está disponible, dentro de ese tramo de N días solo se suman los valores marcados como abiertos. Los cerrados no cuentan en el baseline.
  • Numerador: suma de las predicciones del motor para cada uno de los tres escenarios (conservador, central, optimista).
  • Porcentaje = (numerador - denominador) / denominador × 100.
  • Si el denominador es cero o el histórico está vacío, la tarjeta muestra 0 en lugar de un porcentaje engañoso o un símbolo de infinito.
Implementación: src/lib/prediction-stats/pop.ts (función calculatePoP)
Verificador automático: audit:pop (5 casos sintéticos con distintas combinaciones de días abiertos/cerrados y un caso con datos reales de un comercio con cierre semanal)

6. Cuánto tiempo guardamos tu predicción

Guardamos tu predicción durante 90 días desde el momento en que la desbloqueas. Pasado ese plazo se elimina automáticamente. Puedes descargarla como PDF o JSON en cualquier momento antes de que expire, y el JSON incluye toda la información necesaria para reproducirla sin depender de nuestros servidores.

Reglas exactas:

  • El plazo de 90 días es una constante única llamada PREDICTION_TTL_DAYS. Cambia en un solo lugar y afecta a todos los mensajes al usuario y a la caducidad real. No hay dos plazos distintos en la aplicación.
  • Las predicciones de usuarios en periodo de prueba (sin cuenta) están sujetas al mismo plazo. Los identificadores anónimos usados para prevenir abuso se borran a los 90 días de inactividad.
  • El JSON de descarga incluye la versión del esquema (schemaVersion) y del motor (engineVersion) usadas para generarla. Aunque el formato cambie en el futuro, tu descarga sigue siendo interpretable porque lleva su versión dentro.
Implementación: src/config/constants.ts (constante PREDICTION_TTL_DAYS = 90), src/app/api/credits/spend/route.ts (fija expiresAt), src/lib/trials.ts (mismo TTL para trials)

7. Lo que NUNCA hacemos

El motor Chronos 2 es un modelo pre-entrenado por Amazon Science, con inferencia zero-shot: predice sobre tu serie temporal sin haber sido reentrenado con tus datos. Ni nosotros ni ApisDom reentrenamos el modelo, y por diseño técnico no hay ningún punto del sistema donde tus datos formen parte de un dataset colectivo.

Reglas exactas:

  • No hacemos fine-tuning ni entrenamiento incremental con tus datos. La inferencia es zero-shot por diseño.
  • No vendemos ni cedemos tus datos a terceros con fines de marketing o publicidad.
  • No cruzamos tus datos con los de otros usuarios para construir estadísticas agregadas ni benchmarks.
  • No conservamos tu archivo original después de calcular la predicción. Solo guardamos el resultado (fechas, valores, escenarios y metadatos necesarios para reproducirlo dentro del plazo de 90 días).
  • No enviamos correos comerciales sin tu consentimiento explícito. El newsletter es opt-in con doble confirmación.

Sobre las comprobaciones automáticas

El comando npm run verify:all agrupa las comprobaciones del proyecto: verificadores estáticos (detectan patrones peligrosos en el código), chivatos dinámicos (ejecutan las funciones críticas con datos reales) y consistencia de tipos. El pipeline distingue entre fallos bloqueantes, comportamientos conocidos aceptados de forma explícita y pruebas todavía pendientes: los primeros detienen el despliegue, los otros quedan registrados en el propio pipeline para revisión posterior. Para las condiciones legales completas del tratamiento, revisa la política de privacidad y los términos de servicio.