# 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 en `localStorage` con lazy initializer en `useState` - `ReportPage.tsx`: reemplaza `useState('relative')` por `useHeatmapMode()` - `BpmnCanvas.tsx`: zoom-in/zoom-out movidos de `right:12` a `left: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): `mode` ya estaba como dependencia del `useEffect` en `HeatmapCanvas.tsx` desde 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; `ReportPage` usa el hook. - Fix 3 (zoom position): `left: 12` en `BpmnCanvas.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.ts` expone `getProcessCountsByResourceId()` y `getProcessesByResourceId()` para carga on-demand. - Perfil de usuario: campo `company` en tabla `users` (Supabase), campo en `ProfileSheet`, visible en dropdown `AppHeader`. - Fix `||` vs `??` en cadena de propagación de `name`: `buildAuthUser`, `getUserFromStorage`, `syncProfileFromDB`, `AuthContext useEffect` — evita que Google OAuth con `full_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"): 1. **Contención de `processLock`:** `supabase.from().update()` en `ProfileSheet` adquiría el lock vía `getSession()` interno, bloqueando otras queries de la página. Fix: reemplazo por `fetch()` nativo con token leído de `localStorage` (`supabase.auth.token`), sin pasar por el cliente Supabase. 2. **`@radix-ui/react-remove-scroll` no limpiaba `pointer-events:none` del body:** al cerrar el Sheet, el cleanup de Radix no corría porque el re-render de `AuthContext` (disparado por `updateUser`) interrumpía la animación de cierre. `document.body.style.pointerEvents = 'none'` quedaba permanente, bloqueando toda la UI excepto el canvas BPMN (que fija `pointer-events: all` en su propio SVG). Fix: wrapper `Sheet` en `src/components/ui/sheet.tsx` con `useEffect` que observa el prop `open` y 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` — `||` en `buildAuthUser` y `syncProfileFromDB` - `src/auth/AuthContext.tsx` — `||` en `getUserFromStorage` y `useEffect` de sync - `src/components/ProfileSheet.tsx` — fetch nativo, optimistic close, defer `updateUser` 350ms - `src/components/ui/sheet.tsx` — wrapper defensivo con cleanup de `pointer-events` - `src/lib/supabase.ts` — `lockAcquireTimeout: 30000`, eliminación de `getSessionSafe()` - `src/store/library-store.ts`, `src/store/process-store.ts`, `src/features/report/useReportData.ts` — eliminación de `getSessionSafe()` --- ### [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 `` 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.md` o `BACKLOG.md` segú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 ```