10 KiB
Sprint 6 — BRIEF
InQ ROI — Client-readiness polish + análisis de sensibilidad Inicio: 2026-06-23 Foco: primer uso real con cliente externo como norte operativo
Contexto
Sprint 5 cerró con el producto estable, en uso interno y con el reporte de ROI funcional. La pregunta que orientó la apertura de Sprint 6 fue: ¿qué le falta a InQ ROI para correrlo en vivo frente a un cliente y producir un informe de ROI creíble hoy?
Diagnóstico al cierre de Sprint 5:
- El motor de simulación, el heatmap, la ComparisonTable y los 3 PDFs están listos.
- El consultor puede preparar el proceso con anticipación y correr la simulación en reunión.
- Dos gaps concretos antes de la primera sesión con cliente externo:
- Sin logo en el header de los PDFs — el entregable parece borrador sin marca visual.
- Sin análisis de sensibilidad — el ROI es un número puntual; cualquier cliente sofisticado pregunta "¿y si el volumen cae 20%?" y no hay respuesta dentro de la herramienta.
Sprint 6 cierra esos dos gaps.
Etapas
Etapa 1 — Logo InQuality en header de PDFs + whitespace-nowrap
Prioridad: bloqueante para entregable a cliente externo.
Logo en PDF header:
Los assets ya existen en assets/. Cuatro PNGs disponibles — usar el horizontal color para fondos claros:
MARCA FINAL 2023 AREA DE SEGURIDAD 1 COLOR.png— candidato principal (header blanco)MARCA FINAL 2023 AREA DE SEGURIDAD 1 BCO.png— alternativa para headers con fondo oscuro (si aplica)
El logo va en el header compartido de los 3 PDFs (Actual, Automatizado, ROI). La función drawSharedHeader en src/lib/export/pdf-sections-shared.ts es el punto de inserción.
Implementación:
Convertir el PNG a base64 en tiempo de ejecución usando fetch + arrayBuffer → btoa, o pre-convertirlo como asset importado con ?url de Vite. Insertar con doc.addImage(base64, 'PNG', x, y, width, height). El logo reemplaza o complementa el texto "InQuality" que pueda existir actualmente en el header.
Restricciones:
- Ancho máximo: ~35mm (no puede dominar el header, el título del proceso sigue siendo protagonista)
- Alineación: derecha del header (o izquierda — validar visualmente con el director antes del OK formal)
- Mantener "Powered by InQuality" en el footer — no reemplaza, complementa
Fold-in — whitespace-nowrap en celda de Eficiencia:
En src/features/report/ComparisonTable.tsx, en el <span> del número dentro de MetricCell para la columna Eficiencia, agregar whitespace-nowrap para que "1.02 USD/min" no se corte en dos líneas.
Una línea de cambio. Sin tests que actualizar.
Criterios:
- Logo visible en el header de los 3 PDFs exportados
- Tamaño y posición validados visualmente por el director
- "Powered by InQuality" sigue en el footer de todas las páginas
- "1.02 USD/min" aparece en una sola línea en ComparisonTable
npm run lintsin erroresnpm run buildlimpio- Tests Vitest verdes (sin regresiones)
- Validación visual del director sobre el PDF antes del OK formal
Etapa 2 — Panel de análisis de sensibilidad
Contexto del problema:
El ROI que calcula InQ ROI hoy es nominal y puntual: dadas estas condiciones exactas, el retorno es X. Pero las condiciones de un proceso real tienen incertidumbre — el volumen puede variar, la inversión puede dispararse, las estimaciones de tiempo pueden estar erradas. Un consultor que presente un ROI de USD 1.2M sin poder responder "¿y si el volumen cae 30%?" pierde credibilidad frente a cualquier CFO.
Qué NO hacer: re-simulación completa (implica cargar el BPMN, recorrer gateways, recalcular recursos — es lento y no necesario). Los costos por instancia del proceso ya están calculados y son estables. Solo cambian los agregados anuales.
Qué SÍ hacer: un panel que recalcula únicamente las fórmulas de ROI (domain/roi.ts) con inputs modificados por sliders — sin tocar el motor de simulación. El cálculo es instantáneo.
Variables de sensibilidad (dos sliders):
-
Volumen (frecuencia anual): rango -50% a +100%, paso 10%, default 0% (= valor configurado en el proceso). Afecta: ahorro anual, ROI, payback, trayectoria acumulada.
-
Inversión en automatización: rango -50% a +100%, paso 10%, default 0% (= valor configurado). Afecta: ROI, payback, trayectoria acumulada. No afecta el ahorro por ejecución.
UX — versión mínima:
- Panel colapsable "Análisis de sensibilidad" debajo de los KPIs de ROI en la pestaña "Comparación + ROI"
- Dos sliders con label, valor actual y delta en porcentaje (ej: "Volumen: 12.000 ejecuciones/año (+20%)")
- Los 4 KPIs de ROI (Ahorro por ejecución, Ahorro anual, Payback, ROI anual) se actualizan en tiempo real al mover los sliders
- Badge o indicador visual que muestra que los valores están en modo "análisis" (no son los valores base)
- Botón "Restablecer" que vuelve a los valores originales del proceso
UX — versión wow (+criterio de wow):
Además de la versión mínima, agregar una visualización de rango: para la variable más sensible (volumen), mostrar una banda de incertidumbre en el CumulativeSavingsChart existente — la línea actual se convierte en el caso base, y se agrega un área sombreada que muestra el rango [-30%, +30%] en el mismo gráfico. Misma paleta naranja, opacidad reducida para el rango. Esto convierte el gráfico de trayectoria de "certeza" a "escenario con rango", que es la forma en que un consultor financiero presenta proyecciones.
Implementar versión wow si el costo extra es < 40% del tiempo de la versión mínima.
Implementación técnica:
- El estado de los sliders vive en un
useStatelocal enReportPage.tsxo en un componenteSensitivityPanelnuevo — no persiste (se resetea al recargar, es análisis exploratorio) - El recálculo de KPIs se hace importando las funciones de
domain/roi.tsdirectamente, pasando los inputs modificados CumulativeSavingsChartrecibe los datos recalculados vía props — no requiere cambios internos si ya aceptaroi: RoiResultcomo prop- El panel NO se exporta al PDF en esta etapa (es una feature interactiva del reporte web)
Restricciones:
- NO re-simular (no llamar al motor BPMN ni a
simulation.ts) - NO persistir en Supabase (es exploración, no configuración)
- NO tocar
domain/roi.ts(solo consumirlo) - El valor base del proceso (frecuencia, inversión) NO cambia en base de datos
Criterios:
- Panel visible en la pestaña "Comparación + ROI", colapsado por defecto
- Slider de volumen: -50% a +100%, los KPIs se actualizan en tiempo real
- Slider de inversión: -50% a +100%, los KPIs se actualizan en tiempo real
- Los valores base (sin ajuste de sliders) son idénticos a los KPIs actuales
- Botón "Restablecer" vuelve los sliders a 0%
- Indicador visual de "modo análisis" activo
- (Wow) Banda de rango visible en CumulativeSavingsChart si se implementa
- Tests unitarios del recálculo de sensibilidad en Vitest
npm run lintsin erroresnpm run buildlimpio- Validación visual del director antes del OK formal
Etapa 3 — Quick wins y limpieza técnica
3a — Investigación auth E2E localStorage (TECH_DEBT [INVESTIGATING]):
Contradicción documentada en TECH_DEBT.md: en producción, ProfileSheet lee supabase.auth.token; en E2E, auth-setup.ts escribe sb-${projectRef}-auth-token y los smoke tests pasan. Determinar definitivamente qué clave usa el cliente Supabase de la app en producción comparando el localStorage real vs el auth-state.json.
Acción concreta: agregar un spec E2E que haga una query autenticada a Supabase y afirme que devuelve el email real del usuario de test (no vacío). Si el test pasa, el auth E2E funciona realmente. Si falla, el bug está confirmado.
3b — Cleanup setTimeout(350) en ProfileSheet:
src/components/ProfileSheet.tsx tiene un setTimeout(350) que difiere updateUser como mitigación de la race condition con Radix. El fix definitivo vive en sheet.tsx (useEffect sobre prop open). El setTimeout es redundante pero inofensivo.
Eliminar el setTimeout, verificar que el fix de sheet.tsx sea suficiente. Un test manual de guardar perfil confirma que funciona.
3c — Animación de expansión sección "Automatización" en ActivityPanel:
La sección usa el truco de max-height para la animación de expand/collapse. La aceleración no es lineal en los últimos píxeles (se nota el "freno" al final).
Fix: usar useRef + scrollHeight para animar a la altura real del contenido con una transición CSS. Impacto: micro-pulido visual, moderado esfuerzo.
Solo implementar si 3a y 3b no consumen el slot completo de la etapa.
Criterios:
- Spec E2E de auth: verde si el auth funciona, o diagnóstico definitivo + fix si no funciona
setTimeout(350)eliminado de ProfileSheet, funcionalidad verificada manualmente- TECH_DEBT.md actualizado: marcar RESOLVED los ítems cerrados
- (Opcional) Animación de expansión más fluida en ActivityPanel
npm run test→ mantener o superar 542 tests verdes- Validación del director sobre el estado de la documentación
Distribución del sprint
| Área | Etapas | % estimado |
|---|---|---|
| PDF (logo) | 1 parcial | ~15% |
| Web app features (sensibilidad) | 2 | ~65% |
| Infraestructura / deuda técnica | 3 | ~20% |
Dentro del rango de rebalanceo comprometido (60-70% web, 15-25% PDF, 10-15% infra).
Qué NO entra en Sprint 6
- Historial de simulaciones / múltiples simulaciones por proceso
- Modelo B (dos BPMNs AS-IS / TO-BE)
- Export Excel
- Modo offline / PWA
- i18n
- Compartir procesos con usuarios externos (
setShared()) - Keycloak / migración de auth
- Análisis de sensibilidad en PDF (es feature interactiva, PDF es estático)
Definition of Done del sprint
- 3 etapas con OK formal del director (validación visual de Etapas 1 y 2; validación documental de Etapa 3)
npm run buildlimpionpm run lintsin warnings nuevos- Tests Vitest: mantener o superar 542
- OBSERVATIONS.md con hallazgos del sprint
- STRATEGIC_DIRECTION.md actualizado al cerrar el sprint