10 KiB
OBSERVATIONS — Sprint 5
Hallazgos descubiertos durante el sprint actual. Este archivo se vacía al cerrar el sprint: los hallazgos se promocionan a TECH_DEBT.md o BACKLOG.md, o quedan resueltos.
Sprint 4 cerrado el 2026-06-18. Observaciones archivadas — ítems migrados a BACKLOG.md y TECH_DEBT.md.
Convención de etiquetas
[CURRENT-SPRINT]— Se resuelve en este sprint[DEFERRED-TECH-DEBT]— Se va a TECH_DEBT.md al cerrar el sprint[DEFERRED-BACKLOG]— Se va a BACKLOG.md al cerrar el sprint[RESOLVED]— Resuelto durante el sprint, archivar al cierre
Hallazgos activos
[RESOLVED] Etapa 1 — Workspace loading fix + carry-over skeleton
Estado: OK formal del director — 2026-06-19.
Fix principal: process-store.ts y useReportData.ts usaban getSession() sin timeout; reemplazados inicialmente por getSessionSafe(). Error Boundary + estado error inline con retry button agregados.
⚠️ Corrección histórica (Etapa 4E): getSessionSafe() fue identificado como contraproducente y eliminado completamente. Usaba Promise.race con un timeout que abandonaba la Promise interna de getSession(), dejando operaciones huérfanas ocupando el processLock indefinidamente. El fix correcto fue lockAcquireTimeout: 30000 en supabase.ts + eliminación de getSessionSafe() en todos los archivos. El código actual NO usa getSessionSafe().
Carry-over a Etapa 2: WorkspaceSkeleton.tsx fue creado pero el spinner del router-level guard (TanStack Router) intercepta antes de que el componente monte. El skeleton no aparece en la práctica. Corregir en Etapa 2 como Fix 0 (bajo esfuerzo).
SupabaseAuthService.ts: su getSession() directo documentado como excepción intencional (necesita el objeto sesión real, no es barrera descartable). Sin callers activos. Ver TECH_DEBT.md para el ítem pendiente.
Hallazgos resueltos durante el sprint
[RESOLVED] Etapas 3 + 3B — Heatmap gradiente + toggle + persistencia
Estado: OK formal del director — 2026-06-19.
Entregado: gradiente 4 stops (#10b981→#f59e0b→#f97316→#ef4444), modos percentile/minmax, useHeatmapMode hook con localStorage, HeatmapModeToggle, leyenda PDF 4 stops, zoom controls movidos a bottom-left.
Lección operativa: Dokploy con Autodeploy desactivado requiere git push origin main explícito antes de validar en producción. Los commits a65bec4 y e8a4a1b estaban locales sin pushear — Dokploy desplegaba Etapa 2 mientras se validaba Etapa 3. La validación fallida fue contra código desactualizado, no contra bugs reales.
[RESOLVED] Etapa 2 — Zoom BPMN fix + controles + skeleton
Estado: OK formal del director — 2026-06-19.
Fixes: fit-viewport 'auto' agregado (una línea), zoom-in/zoom-out en esquina inferior derecha (Lucide icons, 1.2x factor, límites 0.1x–4x), skeleton wired correctamente en WorkspacePage (guard isLoading simple sin !currentProcess).
Carry-over a Etapa 3: controles de zoom solapan el logo bpmn.io (esquina inferior derecha). Fix: mover a esquina inferior izquierda (left: 12 en lugar de right: 12). Una línea CSS.
[RESOLVED] Etapa 3 — Heatmap gradiente 4 stops + persistencia de modo + zoom position
Estado: pendiente OK formal del director. Fixes Etapa 3:
colors.ts: gradiente continuo verde→amarillo→naranja→rojo (interpolación genérica N-stops), modos'percentile'/'minmax'reemplazan'relative'/'absolute'useHeatmapMode.ts(hook nuevo): persiste preferencia enlocalStoragecon lazy initializer enuseStateReportPage.tsx: reemplazauseState<HeatmapMode>('relative')poruseHeatmapMode()BpmnCanvas.tsx: zoom-in/zoom-out movidos deright:12aleft:12(evita solapar logo bpmn.io)- PDF: leyenda de 4 stops con interpolación. Tests: 528 Vitest verdes. E2E recalibrados con distancia euclidiana.
Fixes Etapa 3B verificados (incluidos en Etapa 3):
- Fix 1 (repintado de canvas al cambiar toggle):
modeya estaba como dependencia deluseEffectenHeatmapCanvas.tsxdesde el MVP. El bug fue observado durante implementación intermedia; resuelto con los renombres de Etapa 3. - Fix 2 (persistencia localStorage):
useHeatmapMode()con lazy initializer correcto;ReportPageusa el hook. - Fix 3 (zoom position):
left: 12enBpmnCanvas.tsx.
[RESOLVED] Etapa 4 — Recursos por proceso + perfil empresa + fix freeze ProfileSheet
Estado: OK formal del director — 2026-06-23.
Entregado:
ResourceCatalogPage: columna "Procesos" con conteo de procesos que usan cada recurso.resource-repo.tsexponegetProcessCountsByResourceId()ygetProcessesByResourceId()para carga on-demand.- Perfil de usuario: campo
companyen tablausers(Supabase), campo enProfileSheet, visible en dropdownAppHeader. - Fix
||vs??en cadena de propagación dename:buildAuthUser,getUserFromStorage,syncProfileFromDB,AuthContext useEffect— evita que Google OAuth confull_name: ""deje el botón Guardar deshabilitado.
Bug crítico resuelto — freeze de UI al guardar perfil (12 commits, 2026-06-23):
Dos causas independientes bajo el mismo síntoma ("la app se congela"):
-
Contención de
processLock:supabase.from().update()enProfileSheetadquiría el lock víagetSession()interno, bloqueando otras queries de la página. Fix: reemplazo porfetch()nativo con token leído delocalStorage(supabase.auth.token), sin pasar por el cliente Supabase. -
@radix-ui/react-remove-scrollno limpiabapointer-events:nonedel body: al cerrar el Sheet, el cleanup de Radix no corría porque el re-render deAuthContext(disparado porupdateUser) interrumpía la animación de cierre.document.body.style.pointerEvents = 'none'quedaba permanente, bloqueando toda la UI excepto el canvas BPMN (que fijapointer-events: allen su propio SVG). Fix: wrapperSheetensrc/components/ui/sheet.tsxconuseEffectque observa el propopeny limpia el body 400ms después del cierre si no hay otro dialog abierto.
Lección operativa (cosechada a methodology/): ante un bug de UI que persiste tras 2+ fixes teóricos basados en lectura de código, pedir evidencia observable directa (DOM, Network, consola) antes de continuar. El diagnóstico definitivo llegó de un dato: document.body.style.pointerEvents = 'none'. Ver INFORME_ETAPA_4_FREEZE_PERFIL.md para cronología completa.
Archivos modificados:
src/auth/SupabaseAuthService.ts—||enbuildAuthUserysyncProfileFromDBsrc/auth/AuthContext.tsx—||engetUserFromStorageyuseEffectde syncsrc/components/ProfileSheet.tsx— fetch nativo, optimistic close, deferupdateUser350mssrc/components/ui/sheet.tsx— wrapper defensivo con cleanup depointer-eventssrc/lib/supabase.ts—lockAcquireTimeout: 30000, eliminación degetSessionSafe()src/store/library-store.ts,src/store/process-store.ts,src/features/report/useReportData.ts— eliminación degetSessionSafe()
[RESOLVED] Etapa 5 — Columna "Eficiencia de automatización" en ComparisonTable
Estado: implementado, tests verdes (542 Vitest). Pendiente OK formal del director.
Entregado: dos columnas A/B para focus group — "Ahorro esperado" (ahorro$ × executionProbability) y "Eficiencia" (ahorro$ × executionProbability / executionTimeMinutes, formato valor currency/min). Sort multi-columna con headers clickeables, tooltips explicativos, barras de progreso proporcionales (MetricCell). PDF explícitamente fuera de scope (6ª columna no entra en 138mm de ancho).
Desviación de scope necesaria: cambiar el sort por defecto a efficiency rompía un test existente en roi-components.test.tsx que asumía que un solo click invertía el orden. Ajustado a doble-click (activar columna → invertir) para ejercitar el comportamiento real sin dejar la suite roja — el propio prompt exigía npm run test verde.
Carry-over a BACKLOG: celda de Eficiencia corta el valor+unidad en dos líneas por ancho — falta whitespace-nowrap en el <span> de MetricCell.
[RESOLVED] Etapa 6 — Limpieza técnica: E2E Supabase + flakiness + documentación
Estado: implementado, 542 Vitest + 9 smoke E2E + 6 specs de payback/ROI (3 corridas) verdes. Pendiente OK formal del director.
Diagnóstico 6a-extra (auth-setup.ts): bug NO confirmado. auth-js define STORAGE_KEY='supabase.auth.token' como default de GoTrueClient, pero @supabase/supabase-js's createClient() lo sobrescribe calculando sb-${projectRef}-auth-token desde la URL — exactamente la clave que ya usaba auth-setup.ts. Confirmado empíricamente: smoke test "app header shows user avatar" (aserción no-vacía, requiere sesión real) pasa; auth-state.json generado contiene la clave esperada. Documentado inline en auth-setup.ts.
6b — data-testid + fix flakiness: data-testid="save-activity-btn" en ActivityPanel.tsx. payback-etapa-2.spec.ts y export-roi.spec.ts reemplazan click directo por waitForSelector(':not([disabled])') → click → waitForSelector('[disabled]'). Confirmado estable 3/3 corridas tras rebuild (el webServer de Playwright sirve dist/ sin rebuildear — hay que npm run build antes de cada corrida E2E tras tocar src/).
6a — E2E Dexie→Supabase: export-pdf.spec.ts (test "Heatmap con gradiente real") y validate-dod-etapa4.spec.ts reescritos con service_role_key. Sin la key en .env.test, ambos hacen test.skip() con mensaje descriptivo — confirmado (2 skipped, 0 failed). afterAll limpia el proceso creado.
Desviación de scope explícita: se encontraron 9 specs adicionales + GlobalSettingsPanel.tsx con el mismo patrón flaky de "Guardar" sin data-testid. No tocados — el prompt listaba solo 2 specs y 1 archivo de producción. Documentado en TECH_DEBT.md como ítems nuevos.
Hallazgos diferidos (promocionar al cerrar sprint)
(vacío)
Notas operativas
- Los hallazgos resueltos quedan acá hasta el cierre del sprint. Al cerrar, se archiva o purga.
- Los hallazgos sin resolver se promocionan a
TECH_DEBT.mdoBACKLOG.mdsegún corresponda. - Este archivo NO contiene roadmap futuro ni deuda histórica — solo hallazgos del sprint en curso.
Cierre del sprint (checklist)
Cuando el sprint cierre, ejecutar:
[ ] Verificar que todos los items [CURRENT-SPRINT] están RESOLVED
[ ] Mover items [DEFERRED-TECH-DEBT] a TECH_DEBT.md (si no están ya)
[ ] Mover items [DEFERRED-BACKLOG] a BACKLOG.md (si no están ya)
[ ] Archivar o purgar este archivo
[ ] Crear OBSERVATIONS.md vacío para el sprint siguiente