Files
inq-roi-simulador-web/OBSERVATIONS.md
2026-06-23 22:33:01 -03:00

135 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.1x4x), 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<HeatmapMode>('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 `<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.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
```