# ETAPA 2E — Refactorización del dashboard alineada al instrumento real ## 1. Objetivo Corregir la deuda semántica de Etapa 2D: las RPC, KPIs y subtítulos del dashboard se construyeron a partir de códigos de pregunta inventados en el prompt original, no de los códigos reales de `sprints/sprint-1/SEED_SPEC.md`. Esto generó métricas mal etiquetadas que el dashboard mostraría como verdaderas cuando lleguen datos reales. **Esta etapa toca la capa de datos (Nivel 3, autorizado por el director).** Reescribe RPCs, agrega una pregunta nueva al instrumento, elimina un KPI sin sustento, redefine un widget y renombra otro. --- ## 2. Contexto previo - Etapa 2D entregada con subtítulos basados en códigos inventados. - El director auditó subtítulo por subtítulo contra `SEED_SPEC.md` y encontró que 11 de 12 widgets/KPIs tenían discrepancias. - El instrumento real (`SEED_SPEC.md` v2) tiene 44 preguntas. Está en el repo. - En base de Cloud hay respuestas de testing — se truncan en esta etapa. --- ## 3. DECISIONES YA TOMADAS — no consultar de nuevo ### 3.1 Truncado de datos de testing Antes de aplicar migraciones nuevas, **borrar todas las respuestas de testing** del survey piloto. Mantener `surveys`, `questions`, `consent_versions`. Borrar: - `responses` del survey `30000000-0000-0000-0000-000000000001` - `answers` asociadas - `consent_records` asociados ### 3.2 Nueva pregunta del instrumento — A1b Departamento Agregar al seed, **antes** de A2 (Ciudad): ``` A1b · single_choice · "¿En qué departamento de Paraguay vivís?" * Opciones (18 departamentos): Asunción · Central · Alto Paraná · Itapúa · Caaguazú · San Pedro · Cordillera · Paraguarí · Concepción · Guairá · Caazapá · Misiones · Ñeembucú · Amambay · Canindeyú · Presidente Hayes · Boquerón · Alto Paraguay ``` - `qi_zone` ← A1b (cuasi-identificador para el k-anonimato). - A1 (País) **se mantiene** por ahora (lo decidiremos en BACKLOG). ### 3.3 KPI 4 (NPS) — ELIMINADO El instrumento no tiene pregunta NPS. Eliminar el KPI completamente del layout. El grid de KPIs queda en **3 cards**, no 4. Quitar `rpc_nps_*` si existe. ### 3.4 Widget 6 (Políticas) — REDEFINIDO El instrumento no captura "políticas que la empresa ya ofrece". Eliminar el componente de comparación. El widget muestra **solo demanda** desde 6.2 (apoyos que ayudarían más, multi-select, top 8). ### 3.5 Widget 8 — RENOMBRADO "Tabla de riesgo de retención" → **"Saturación del cuidador"**. El instrumento no captura intención de salida directa; el indicador compuesto mide saturación, no intención de irse. Cambiar título y subtítulo. ### 3.6 Mapeo final widget → códigos del seed real **KPI Cards (3, sin NPS)** | # | Título | Definición y código real | Subtítulo | |---|---|---|---| | K1 | CUIDADORES ACTIVOS | % con `1.6='Sí'` OR `1.13='Sí'` | `1.6 + 1.13 · % de la plantilla con responsabilidades de cuidado de otros` | | K2 | FRECUENCIA DE AUSENTISMO | % con `5.2 IN ('Varias veces (4–10 veces al año)', 'Con frecuencia (más de 10 veces al año)')` | `5.2 · % con ausencias laborales recurrentes (4 o más veces al año)` | | K3 | BIENESTAR | Promedio del rating `4.3` (escala 1–5, mostrar como /5 o como %) | `4.3 · autorreporte de bienestar general · escala 1–5` | **Widgets (9, con W6 redefinido y W8 renombrado)** | # | Título | Códigos reales | Subtítulo | |---|---|---|---| | W1 | Prevalencia de cuidadores por departamento | `A1b` (nueva) cruzado con `1.6 ∨ 1.13` | `A1b · % cuidadores activos por departamento` | | W2 | Perfil de la población | `1.1 + 1.6 + 1.13` (no excluyentes) | `1.1 + 1.6 + 1.13 · discapacidad propia, cuidador familiar y de adulto mayor (no excluyentes)` | | W3 | Madurez organizacional | Score = 0.7 × `6.1_norm` + 0.3 × `6.5_norm`, ambas normalizadas 0–100 | `6.1 + 6.5 · comprensión percibida y expectativa hacia la empresa · score 0–100` | | W4 | Horas de cuidado × Frecuencia de ausentismo | `2.2 × 5.2` | `2.2 × 5.2 · correlación entre intensidad de cuidado y ausentismo recurrente` | | W5 | Pirámide demográfica | `A5 × A6` | `A5 × A6 · rango etario por género (celdas con k ≥ 5)` | | W6 | Apoyos más demandados | `6.2` (multi-select, max 3) | `6.2 · apoyos de la empresa que los respondentes consideran que más los ayudarían` | | W7 | Intensidad de cuidado vs. Bienestar | `2.2 × 4.3` | `2.2 × 4.3 · cruce agregado intensidad de cuidado × bienestar (bins con k ≥ 5)` | | W8 | **Saturación del cuidador** | Compuesto: `4.3 ≤ 2` AND `5.1 = "Casi todos los días"` AND `5.2 ≥ "Varias veces (4–10 veces al año)"` | `4.3 + 5.1 + 5.2 · indicador compuesto de saturación (bienestar bajo + rendimiento afectado + ausentismo)` | **Definición de segmentos para W8 (Saturación):** - **Alta saturación:** los 3 criterios se cumplen - **Saturación media:** 2 de los 3 criterios - **Baja saturación:** 1 o ninguno Columnas de la tabla: Segmento · N · % del total · Apoyo más demandado del segmento (de 6.2). Cada segmento con N < 5 se muestra como "—" (suprimido). ### 3.7 Normalización para Madurez (W3) `6.1` tiene 4 opciones, normalizar: - "Sí, me siento acompañado/a" = 100 - "Parcialmente, hay cierta comprensión" = 66 - "No, es algo bastante invisible" = 33 - "No, nunca se ha hablado del tema" = 0 `6.5` tiene 4 opciones, normalizar: - "Sí, definitivamente" = 100 - "Tal vez, dependería del caso" = 66 - "No lo considero necesario" = 33 - "No lo sé" = 0 (o NULL — decidir en RPC, ver tarea 4.3) Score final = 0.7 × promedio(6.1_norm) + 0.3 × promedio(6.5_norm) --- ## 4. Tareas ### 4.1 Migración 009 — Truncado + nueva pregunta A1b Crear `supabase/migrations/0YYYYMMDDHHMMSS_009_add_departamento_paraguay.sql`: 1. `BEGIN;` 2. `TRUNCATE answers, responses, consent_records CASCADE;` (de Cloud) 3. INSERT en `questions` la pregunta A1b con sus 18 opciones (`question_options`). Posición: entre A1 y A2. 4. Actualizar el orden de las preguntas de la sección A si es necesario. 5. `COMMIT;` Validación post-migración: - `SELECT COUNT(*) FROM answers;` → 0 - `SELECT code, text FROM questions WHERE code='A1b';` → 1 fila - `SELECT COUNT(*) FROM question_options qo JOIN questions q ON qo.question_id=q.id WHERE q.code='A1b';` → 18 ### 4.2 Migración 010 — Refactor de RPCs del dashboard Crear `supabase/migrations/0YYYYMMDDHHMMSS_010_dashboard_rpc_v2.sql`. DROP y recreate todas las funciones del dashboard con los nuevos mapeos. Lista: - `rpc_dashboard_kpis` — devuelve 3 KPIs (sin NPS), con los códigos correctos - `rpc_prevalencia_por_depto` — usa A1b, NO A1 - `rpc_perfil_poblacion` (antes `rpc_distribucion_cuidado`) — 1.1 + 1.6 + 1.13 - `rpc_score_organizacional` — nueva fórmula 70/30 - `rpc_heatmap_intensidad` — 2.2 × 5.2 - `rpc_piramide_demografica` — sin cambios estructurales - `rpc_apoyos_demandados` (antes `rpc_politicas_gap`) — solo 6.2, sin comparación - `rpc_intensidad_vs_bienestar` (antes `rpc_intensidad_vs_productividad`) — 2.2 × 4.3 - `rpc_saturacion_cuidador` (antes `rpc_riesgo_retencion`) — 4.3 + 5.1 + 5.2 **Todas las RPC mantienen:** - `SECURITY DEFINER` con `search_path` fijado - Supresión `k ≥ 5` por construcción (devuelven `status='suppressed'` si N<5) - Casteo seguro de TEXT a numérico cuando aplique - Filtros globales (departamento ahora viene de A1b, no A1) **El sidebar de filtros también cambia:** - "Departamento" ahora hace dropdown contra las 18 opciones reales de A1b - "Género" sigue contra A6 - "Modalidad" sigue contra A7 - "Etario" sigue contra A5 ### 4.3 Decisión local: cómo tratar "No lo sé" en 6.5 Si el agente lo decide localmente, **debe reportar la decisión** como Nivel 2. Opciones razonables: - Tratar como NULL → no entra al promedio - Tratar como 0 → baja el score **Recomendación:** tratar como NULL (no entra al promedio) — "no lo sé" no es opinión. ### 4.4 Cliente — eliminar K4 (NPS) - Quitar el componente del grid de KPIs - Reajustar el layout del grid (de 4 cols a 3 cols, o 3 cards en 4 cols con la cuarta vacía) - Eliminar tipo TypeScript asociado y query del cliente - `grep -r "NPS\|nps" components/dashboard/ app/dashboard/ lib/dashboard*` debe retornar vacío después ### 4.5 Cliente — redefinir Widget 6 (Apoyos) - Renombrar archivo `politicas-bar.tsx` → `apoyos-bar.tsx` (o equivalente) - Eliminar la lógica de comparación "demandadas vs ofrecidas" - Mostrar solo barras horizontales con top 8 de 6.2 (multi-select, max 3 por respondente) - Color único: `#43BBC8`. Eliminar el color secundario `#F8AD13` para "ofrecidas". ### 4.6 Cliente — renombrar Widget 8 (Saturación) - Renombrar archivo `tabla-riesgo.tsx` → `tabla-saturacion.tsx` - Título: "Saturación del cuidador" (no "Riesgo de retención") - Etiquetas de segmentos: "Alta saturación" / "Saturación media" / "Baja saturación" - Columnas: Segmento · N · % del total · Apoyo más demandado ### 4.7 Cliente — aplicar todos los subtítulos correctos Reemplazar los 11 subtítulos con los textos exactos de la tabla en sección 3.6. ### 4.8 Documentar deuda técnica Agregar a `TECH_DEBT.md` (o crearlo si no existe) los siguientes ítems: ```markdown [TECH-DEBT-002] KPI NPS Cuidador - Estado: el instrumento actual no tiene pregunta NPS. - Si en el futuro se quiere medir NPS hacia la empresa de cuidadores, agregar pregunta en próxima versión del instrumento: "En una escala de 0 a 10, ¿qué tan probable es que recomiendes esta empresa a otra persona cuidadora?" - Hasta entonces, el KPI no existe en el dashboard. [TECH-DEBT-003] Brecha de políticas (oferta vs demanda) - Estado: el instrumento captura solo demanda (6.2 = apoyos que ayudarían). - No captura qué políticas la empresa ya ofrece. - Para construir el widget de brecha, agregar pregunta en próxima versión: "¿Qué apoyos para el cuidado ofrece tu empresa hoy?" (multi-select con las mismas opciones de 6.2 + "Ninguno" + "No lo sé"). - Hasta entonces, W6 muestra solo demanda. [TECH-DEBT-004] Intención directa de salida laboral - Estado: el instrumento no pregunta directamente "¿considerás dejar este trabajo?". - El widget W8 usa indicador compuesto (4.3 + 5.1 + 5.2) renombrado a "Saturación". - Para construir un indicador de intención de salida real, agregar pregunta: "En los últimos 6 meses, ¿pensaste en dejar este empleo por razones relacionadas al cuidado?" (Sí/Frecuentemente · Sí/A veces · No · Prefiero no decir). [TECH-DEBT-005] A1 (País) vs A1b (Departamento) - A1 sigue presente en el seed después de Etapa 2E. - En contexto del piloto (Paraguay únicamente), A1 es redundante: 100% responde Paraguay. - Decidir antes de Sprint 3: eliminar A1 del instrumento, o mantenerlo si la plataforma se va a regionalizar (Uruguay, Bolivia, etc.) en el corto plazo. ``` ### 4.9 Verificación cruzada — patrón nuevo C.8 Antes de declarar terminado, el agente ejecuta este check (Patrón C.8 propuesto): ```bash # Cada subtítulo referenciado en el cliente debe tener su código documentado en SEED_SPEC.md grep -oE '"[0-9A-Z]+(\.[0-9]+)?b?\s*[·+×]' components/dashboard/*.tsx | \ awk '{print $1}' | sort -u # El agente verifica esta lista contra los códigos de SEED_SPEC.md ``` Si algún código del cliente no está en `SEED_SPEC.md`, **DETENERSE y reportar** antes de declarar terminado. No ejecutar literalmente y delegar al director (eso sería AP.10 — propagación silenciosa de inconsistencia). --- ## 5. Entregables (DoD) - [ ] Migración 009 aplicada en Cloud (truncado + A1b con 18 opciones) - [ ] Migración 010 aplicada en Cloud (9 RPCs refactorizadas) - [ ] Test post-migración: con N=0 (truncado) todas las RPCs devuelven `suppressed` - [ ] Test con N≥5 de datos sintéticos (insertados ad-hoc) — al menos 1 RPC devuelve `ok` - [ ] `grep -r "NPS\|nps" components/dashboard/ app/dashboard/ lib/dashboard*` → vacío - [ ] Subtítulos del cliente match exactamente con la tabla 3.6 del prompt - [ ] Sidebar de filtros: dropdown de Departamento usa A1b, no A1 - [ ] Verificación cruzada (4.9) ejecutada sin discrepancias - [ ] `TECH_DEBT.md` actualizado con TECH-DEBT-002, 003, 004, 005 - [ ] Build sin errores (`next build`) - [ ] `git diff --name-only HEAD` solo en scope (migrations, components/dashboard, lib/dashboard*, TECH_DEBT.md) - [ ] Captura del dashboard en estado `suppressed` compartida (Principio 2) ## 6. Qué reportar ``` Verificado: - Migración 009 aplicada: rows truncados=X, A1b insertada con 18 opciones ✓ - Migración 010 aplicada: 9 RPCs DROPped y CREATEd ✓ - Test N=0: todas las RPCs → suppressed ✓ - Test con datos sintéticos: [lista de RPCs con ok / suppressed] - grep NPS: vacío ✓ - Subtítulos: 13 textos exactos verificados contra tabla 3.6 ✓ - Sidebar Departamento usa A1b ✓ - Verificación cruzada C.8: [resultado] - TECH_DEBT.md: ítems 002–005 agregados ✓ - Build exitoso · diff en scope ✓ Asumido: - Decisión local "No lo sé" en 6.5 → [NULL / 0 / otra] - [lo que no se pudo verificar] Decisiones proactivas (Nivel 2): - [lista] Conflictos de scope detectados (Patrón C.8): - [si los hubo, listarlos AQUÍ y DETENER antes de ejecutar] ``` ## 7. Qué NO hacer - ❌ No tocar el formulario (`/app/(survey)/`, `app/actions/submit-survey.ts`). - ❌ No tocar RLS de write existente (la migración 011 anterior es intocable). - ❌ No agregar dependencias npm. - ❌ No inventar códigos de pregunta. Si un código nombrado en este prompt no coincide con `SEED_SPEC.md`, DETENERSE y reportar (Patrón C.8). NO ejecutar "literalmente" y delegar al director. - ❌ No tocar la estructura de capas (sigue siendo cliente → Route Handler → RPC). - ❌ No agregar widgets nuevos. Esta etapa es refactor, no extensión. - ❌ No resolver ítems del backlog que no sean los 4 listados en sección 4.8. --- ## Apéndice — Patrón nuevo propuesto C.8 Si esta etapa cierra limpia, agregar a `method/PATTERNS.md`: > **C.8 — Verificación de códigos contra fuente única · *deriva de P3, P4 · origen: > 2do proyecto*.** Cuando un prompt menciona códigos de un instrumento, esquema o > contrato (preguntas, columnas, campos), el agente verifica cada código contra la > fuente única (`SEED_SPEC.md`, `schema.sql`, etc.) **antes** de ejecutar. Si > detecta una discrepancia, DETIENE la ejecución y reporta el conflicto. Ejecutar > "literalmente" y delegar la verificación semántica al director es AP.10 > (propagación silenciosa de inconsistencia). Respuesta directa al caso Etapa 2D del > proyecto Datos País.