Ambas funciones deployadas al proyecto inq-roi-desa (xgqbkxcliyjzisoccsur).
Some checks failed
/ Deploy to Cloudflare Pages (push) Has been cancelled
Some checks failed
/ Deploy to Cloudflare Pages (push) Has been cancelled
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
# OBSERVATIONS — Sprint 6
|
||||
# OBSERVATIONS — Sprint 7
|
||||
|
||||
> 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 5 cerrado el 2026-06-23.** Observaciones archivadas — ítems migrados a BACKLOG.md y TECH_DEBT.md.
|
||||
> **Sprint 6 cerrado el 2026-06-24.** Observaciones archivadas — ítems migrados a BACKLOG.md y TECH_DEBT.md.
|
||||
|
||||
---
|
||||
|
||||
@@ -24,32 +24,75 @@
|
||||
|
||||
## Hallazgos resueltos durante el sprint
|
||||
|
||||
### [RESOLVED] Etapa 3 — Fix localStorage key + setTimeout cleanup + specs E2E de auth
|
||||
**Bug real confirmado:** `ProfileSheet.getAccessToken()` leía `'supabase.auth.token'` (clave inexistente en supabase-js v2). El guardado de nombre y empresa nunca llegó a Supabase desde que se implementó ProfileSheet en Sprint 4. Fix: calcular `sb-${projectRef}-auth-token` igual que auth-setup.ts. Verificado end-to-end: PATCH 204 + reload confirma persistencia.
|
||||
**setTimeout(350) eliminado:** fix de sheet.tsx es suficiente por sí solo.
|
||||
**Specs E2E:** `auth-localStorage-key.spec.ts` (diagnóstico de clave) y `auth-real.spec.ts` (validación de auth real). 552 tests, 11/11 E2E verdes.
|
||||
**OK formal del director:** 2026-06-24.
|
||||
### [RESOLVED] Etapa 1 — Migraciones DB + SECURITY DEFINER helpers
|
||||
|
||||
### [RESOLVED] Etapa 1 — Logo InQuality en header de PDFs (págs 2+)
|
||||
**Observación 1:** Claude Code implementó inicialmente el logo con dimensiones hardcodeadas (32×9mm, ratio 3.56:1) cuando el PNG real es 724×474px (ratio 1.53:1) → logo aplastado horizontalmente. Fix: pasar dimensiones reales del bitmap y calcular el alto proporcional en `drawSharedHeader`.
|
||||
**Observación 2:** Claude Code agregó "UN PRODUCTO DE InQ InQuality" y logo en la portada (página 1) sin que estuviera en el scope del prompt — violación de la restricción explícita "No agregar el logo en páginas 1 con su propio diseño de portada." Revertido en fix posterior.
|
||||
**Aprendizaje del método:** cuando el prompt incluye restricciones del tipo "NO hacer X en archivo Y", el agente puede introducir el cambio equivalente en archivos relacionados no listados. Auditar activamente los archivos adyacentes al scope declarado.
|
||||
**Resultado final:** logo isotipo a 15mm de ancho, alto proporcional (~9.8mm), páginas 2+ de los 3 PDFs. Portada intacta. whitespace-nowrap en MetricCell de Eficiencia incluido como fold-in.
|
||||
**OK formal del director:** 2026-06-24.
|
||||
**Policy `activity_resource_assignments`:** el BRIEF asumía `"all_activity_resource_assignments"` como nombre de la policy. La migration 007 la creó como `"all_assignments"`. Claude Code auditó las migraciones 001-013 antes de escribir los DROP POLICY y corrigió el nombre. Sin la corrección, el DROP habría pasado silenciosamente (IF EXISTS) sin eliminar la policy permisiva original, manteniendo acceso sin restricción de rol.
|
||||
|
||||
**Tabla `resources` sin `process_id`:** el brief del ETAPA_1_PROMPT incluía una policy `resources_select` con condición `resources.process_id IS NULL`. La columna no existe desde Sprint 3 (catálogo global). Claude Code detectó la discrepancia y adaptó la policy a `auth.uid() IS NOT NULL`, equivalente funcional para catálogo sin filtro de proceso.
|
||||
|
||||
**Auto-migración `client` → `org_id`:** en el entorno de dev, los 158 procesos existentes no tienen valor en el campo `client` → 0 procesos vinculados a org. Esto es esperado: el campo `client` es opcional y en dev no está poblado con clientes reales. En producción, si hay procesos con `client` no vacío, la auto-migración los vinculará automáticamente al primer deploy.
|
||||
|
||||
**OK formal del director:** 2026-07-06.
|
||||
|
||||
---
|
||||
|
||||
### [RESOLVED] Etapa 2 — Panel de análisis de sensibilidad
|
||||
**Resultado:** SensitivityPanel con dos sliders nativos (volumen -50%/+100%, inversión -50%/+100%), badge "Análisis activo", botón Restablecer. adjustedRoi via useMemo en ReportPage, sin re-simulación. Versión wow implementada: doble línea en CumulativeSavingsChart (base gris dashed + área sombreada + ajustada naranja). 552 tests (542 + 10 nuevos).
|
||||
**Fix post-validación:** layout del tab ROI reordenado — CumulativeSavingsChart sube por encima de ComparisonTable para mantener adyacencia con los sliders y la doble línea del gráfico. ComparisonTable queda como detalle granular al final.
|
||||
**Nota técnica a documentar:** CumulativeSavingsChart deriva la inversión original del base case desde `roiBase.cumulativeSavingsHorizon` algebraicamente en lugar de recibir prop `investmentBase`. Correcto hoy (horizonYears del chart = horizonYears de calculateRoi), frágil si divergen. Registrar en TECH_DEBT.md al cerrar el sprint.
|
||||
**OK formal del director:** 2026-06-24.
|
||||
### [RESOLVED] Etapa 2 — Edge Functions invite-user y revoke-user
|
||||
|
||||
Funciones creadas en `supabase/functions/`. El diseño simplificó el scope del BRIEF (que pedía `delete-user` + `suspend-user` + `unsuspend-user` separadas) a `invite-user` + `revoke-user` (soft suspension a 'guest'). Las limitaciones quedaron documentadas en `UsersPage.tsx`:
|
||||
- "Eliminar" llama `revoke-user` (baja a guest) — eliminación real requiere función futura
|
||||
- "Reactivar" restaura siempre a 'client_editor' — rol previo no se guarda
|
||||
**OK formal implícito del director:** 2026-07-06.
|
||||
|
||||
---
|
||||
|
||||
### [RESOLVED] Etapa 3 — Auth layer: fix rol desde DB + email/password + reset-password
|
||||
|
||||
Fix crítico: `syncProfileFromDB` ahora lee `platform_role` y `org_id` desde DB en lugar de inferir el rol del dominio del email. `AuthUser` tiene `orgId?: string`. `signInWithEmailPassword` implementado. `TOKEN_REFRESHED` no dispara loading. `LoginPage` con form email/password. `ResetPasswordPage` en `/reset-password`. 558 tests (+ 6 nuevos).
|
||||
|
||||
**Deuda identificada:** `PASSWORD_RECOVERY` maneja vía segunda suscripción a `onAuthStateChange` (evitar importación circular). No es bug, pero el patrón de doble suscripción es frágil. Ver ítem en TECH_DEBT.md.
|
||||
**OK formal del director:** 2026-07-06.
|
||||
|
||||
---
|
||||
|
||||
### [RESOLVED] Etapa 4 — Panel de administración
|
||||
|
||||
`/admin/organizations`, `/admin/organizations/$orgId`, `/admin/users` con guard de `platform_admin`. Invitación de usuarios via Edge Function `invite-user`. Suspensión/reactivación via `revoke-user`. Skeletons durante carga. Badge "Tú" en fila propia. Acciones deshabilitadas para self-action. RoleBadge con colores por rol.
|
||||
|
||||
**Deuda identificada:** guard implementado en `AdminLayout` como componente (no en `beforeLoad` de TanStack Router) — puede haber flash breve del contenido antes del redirect. Ver ítem recurrente en TECH_DEBT.md.
|
||||
**OK formal del director:** 2026-07-06 (validación visual con DOM).
|
||||
|
||||
---
|
||||
|
||||
### [RESOLVED] Etapa 5 — UX de roles de cliente y routing
|
||||
|
||||
Guards de rol en `WorkspacePage` e `ImportPage` vía `useEffect`. `client_viewer` redirigido a `/report/$processId` al entrar a workspace. `client_editor` bloqueado de `/import`. Botón "Importar BPMN" oculto para roles de cliente. Botón "Nuevo proceso" oculto para roles de cliente. Panel de grupos oculto para roles de cliente (lista plana). `ReportPage` con `defaultTab = 'roi'` para `client_viewer`. 578 tests (+ 20 nuevos).
|
||||
|
||||
**Bug encontrado en validación:** `group_label = "un Cliente"` guardado como dato corrupto en Supabase → `"2 un clientes"` en UI. Causa: el campo era editable desde Settings y en algún momento se guardó con el artículo. Fix: `normalizeGroupLabel()` en `settings-repo.ts` que limpia artículos iniciales (`un`, `una`, `el`, `la`) al leer y al guardar. Solución transparente sin migración de datos.
|
||||
|
||||
**Deuda identificada (BACKLOG):** Settings page (ícono de tuerca) accesible para `client_editor`. La página es cosmética (solo label de grupos), pero no debería ser visible para roles de cliente. Ítem agregado en BACKLOG.md.
|
||||
|
||||
**Patrón recurrente identificado:** guards de rol implementados tres veces via `useEffect` en lugar de `beforeLoad` de TanStack Router (AdminLayout, WorkspacePage, ImportPage). Funcional pero subóptimo — posible flash antes del redirect. Candidato a TECH_DEBT sprint siguiente.
|
||||
|
||||
**OK formal del director:** 2026-07-06 (validación visual con cambio temporal de rol en Supabase Studio).
|
||||
|
||||
---
|
||||
|
||||
## Hallazgos diferidos (promocionar al cerrar sprint)
|
||||
|
||||
### [DEFERRED-TECH-DEBT] Selector frágil en auth-real.spec.ts
|
||||
`tests/e2e/auth-real.spec.ts` usa `header button[title]` para encontrar el avatar del usuario en AppHeader. Si el atributo `title` cambia, el test rompe sin advertencia. Candidato a agregar `data-testid="app-header-user-btn"` en una etapa futura (mismo patrón que `save-activity-btn`).
|
||||
**Migrar a:** TECH_DEBT.md al cerrar Sprint 6.
|
||||
### [DEFERRED-TECH-DEBT] Guards de rol via `useEffect` en lugar de `beforeLoad` (3 componentes)
|
||||
|
||||
`AdminLayout`, `WorkspacePage`, `ImportPage` — los tres usan `useEffect` para detectar el rol y redirigir. Con `beforeLoad` de TanStack Router, la ruta nunca monta para usuarios sin acceso. Con `useEffect`, el componente monta brevemente antes del redirect (posible flash). No es bug de seguridad (RLS protege los datos), pero es anti-patrón UX y arquitectónico para TanStack Router.
|
||||
**Migrar a:** TECH_DEBT.md al cerrar Sprint 7.
|
||||
|
||||
### [DEFERRED-TECH-DEBT] Doble suscripción `onAuthStateChange` en AuthContext
|
||||
|
||||
AuthContext tiene dos listeners de `onAuthStateChange`: uno principal (SIGNED_IN, SIGNED_OUT, TOKEN_REFRESHED) y uno secundario para `PASSWORD_RECOVERY`. Fue implementado para evitar una importación circular. El patrón correcto es un único listener con switch/case completo. Refactorizar cuando se resuelva la dependencia circular.
|
||||
**Migrar a:** TECH_DEBT.md al cerrar Sprint 7.
|
||||
|
||||
### [DEFERRED-BACKLOG] Settings page accesible para client_editor/client_viewer
|
||||
|
||||
El ícono de configuración (tuerca) en LibraryPage y la ruta `/settings` (si existe) son accesibles para todos los roles. La página solo configura el label de grupos (preferencia cosmética), pero un usuario cliente no debería verla. Ocultar el ícono de configuración y agregar guard de rol en la ruta.
|
||||
**Migrar a:** BACKLOG.md al cerrar Sprint 7 — ya agregado por Claude Code.
|
||||
|
||||
---
|
||||
|
||||
@@ -63,12 +106,10 @@
|
||||
|
||||
## 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)
|
||||
[x] Verificar que todos los items [CURRENT-SPRINT] están RESOLVED
|
||||
[x] Mover items [DEFERRED-TECH-DEBT] a TECH_DEBT.md (pendiente commit)
|
||||
[x] Mover items [DEFERRED-BACKLOG] a BACKLOG.md (ya agregado por Claude Code en Etapa 5)
|
||||
[ ] Archivar o purgar este archivo
|
||||
[ ] Crear OBSERVATIONS.md vacío para el sprint siguiente
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user