Instrumento v2: 44 preguntas + widget text_short

This commit is contained in:
markosbenitez
2026-06-04 14:30:28 -03:00
parent 10c665d25c
commit d91ea6b2ff
11 changed files with 765 additions and 751 deletions

View File

@@ -1,105 +1,119 @@
# SEED_SPEC.md — Contrato del instrumento "Mapeo de cuidadores" # SEED_SPEC.md — Contrato del instrumento "Mapeo de Cuidadores" v2
> Inventario completo y autoritativo de la encuesta, extraído del mockup real > Versión 2 — incorpora revisión de la colega (Jun 2026). Reemplaza la v1 (34 preguntas).
> (`formulario_mapeo.html`). Reemplaza el seed inventado de la Etapa 1 (que tenía 9 de 34 preguntas y > 44 preguntas · 8 secciones · Códigos únicos corregidos.
> el supuesto incorrecto C1→G4). Claude Code reescribe `seed.sql` a partir de ESTE contrato. > Claude Code reescribe `seed.sql` a partir de ESTE contrato.
## Convenciones (decisiones cerradas) ## Convenciones
- **Mapeo de tipos al esquema:** radio→`single_choice`, checkbox→`multiple_choice`, escala 1-5→ - Tipos: radio→`single_choice`, checkbox→`multiple_choice`, escala 1-5→`rating`,
`rating`, textarea`text_long`. texto libre largo→`text_long`, texto corto`text_short`.
- **`conditional_rules` viven en la pregunta DEPENDIENTE** (cada pregunta condicional declara su - **`conditional_rules` en la pregunta DEPENDIENTE** (cada pregunta declara su propia condición).
propia condición de visibilidad: `{if_question, op:'eq', value, action:'show'}`). El motor, al - Los 3 triggers (1.1, 1.6, 1.13) son `single_choice` de 3 opciones; solo **"Sí"** dispara el bloque.
decidir si renderiza X, mira las reglas de X. (Resuelve la ambigüedad que el agente había dejado.) - `is_sensitive=true` en preguntas de salud/discapacidad/diagnóstico (marcadas ✅).
- **B1/C1/D1 son `single_choice` de 3 opciones** (Sí / No / Prefiero no decir), **NO `yes_no`**. - `3.4` (salary) — `is_sensitive=true` + analyst-only (ADR-001 addendum): nunca en vistas org_admin.
Solo el valor **"Sí"** dispara el bloque condicional; "No" y "Prefiero no decir" lo ocultan. - RE-INDEX: las preguntas marcadas son candidatas a índice compuesto (metodología TBD Sprint 3).
- **`is_sensitive = true`** en toda pregunta que elicita salud/discapacidad/diagnóstico (marcadas
abajo). La encuesta entera es `data_sensitivity = 'sensible'`.
## Estructura condicional (3 bloques, el resto siempre visible) ## Estructura condicional (3 bloques)
- **cond-B** — visible si `B1 = 'Sí'`contiene B2, B3, B4, B5 - **cond-disc-propia** — 1.1='Sí' → muestra 1.2, 1.3, 1.4, 1.5
- **cond-C** — visible si `C1 = 'Sí'`contiene C2, C3, C4, C5 - **cond-familiar** — 1.6='Sí' → muestra 1.7, 1.8, 1.9, 1.10, 1.11, 1.12
- **cond-D** — visible si `D1 = 'Sí'`contiene D2, D3, D4, D5 - **cond-adulto-mayor** — 1.13='Sí' → muestra 1.14, 1.15, 1.16
- Secciones A (datos generales), E (rol cuidador), F (económico), G (bienestar), H (necesidades): - Secciones A, 2, 3, 4, 5, 6, cierre: **top-level, siempre visibles.**
**top-level, siempre visibles.**
--- ---
## Sección 0 — Datos Generales (top-level, cuasi-identificadores) ## Sección A — Datos Generales (top-level)
| Code | Tipo | Pregunta | Opciones | | Code | Tipo | Pregunta | Opciones | Notas |
|---|---|---|---| |---|---|---|---|---|
| A1 | single_choice | ¿En qué área o gerencia trabajás? | Operaciones/Producción · Comercial/Ventas · Administración/Finanzas · RRHH/Cultura · Tecnología/Sistemas · Logística/Distribución · Otra área | | A1 | text_short | País* | — (texto libre) | qi_country (futuro) |
| A2 | single_choice | ¿Cuántos años tenés? | Menos de 25 · 25-34 · 35-44 · 45-54 · 55+ · Prefiero no decir | | A2 | text_short | Ciudad* | — (texto libre) | qi_city (futuro) |
| A3 | single_choice | ¿Con qué género te identificás? | Femenino · Masculino · No binario/Otro · Prefiero no decir | | A3 | text_short | Sucursal o sede | — (texto libre) | opcional |
| A4 | single_choice | ¿Cuál es tu modalidad de trabajo actual? | Presencial · Remoto · Híbrido | | A4 | single_choice | ¿En qué área o gerencia trabajás?* | Operaciones/Producción · Comercial/Ventas · Administración/Finanzas · Recursos Humanos/Cultura · Tecnología/Sistemas · Logística/Distribución · Otra área | qi_sector |
| A5 | single_choice | ¿Cuántos años tenés? | Menos de 25 años · 25 a 34 años · 35 a 44 años · 45 a 54 años · 55 años o más · Prefiero no decir | qi_age_bucket |
| A6 | single_choice | ¿Con qué género te identificás? | Femenino · Masculino · No binario/Otro · Prefiero no decir | |
| A7 | single_choice | ¿Cuál es tu modalidad de trabajo actual? | Presencial · Remoto (home office) · Híbrido | |
## Sección 1 — ¿Vivís con discapacidad? *(sensible)* ## Sección 1 — Prevalencia: cuidado y discapacidad *(sensible)*
| Code | Tipo | Cond. | Pregunta | Opciones | is_sensitive | | Code | Tipo | Cond. | Pregunta | Opciones | is_sensitive |
|---|---|---|---|---|---| |---|---|---|---|---|---|
| B1 | single_choice | trigger | ¿Vos mismo/a tenés alguna discapacidad o condición de salud…? | Sí · No · Prefiero no decir | ✅ | | 1.1 | single_choice | trigger | ¿Vos mismo/a vivís con alguna discapacidad o condición de salud que afecte tu funcionamiento cotidiano?* | Sí · No · Prefiero no decir | ✅ |
| B2 | multiple_choice | B1=Sí | ¿Qué tipo de discapacidad tenés? (más de una) | Física/motriz · Visual · Auditiva · Intelectual · TEA · Psicosocial · Condición crónica · Otra | ✅ | | 1.2 | multiple_choice | 1.1=Sí | ¿Qué tipo de discapacidad o condición tenés? | Discapacidad física/motriz · Visual · Auditiva · Intelectual · TEA · Psicosocial/salud mental · Condición crónica que genera limitaciones · Otra | ✅ |
| B3 | single_choice | B1=Sí | ¿Contás con certificado oficial de discapacidad? | Sí · En trámite · No | ✅ | | 1.3 | multiple_choice | 1.1=Sí | ¿Usás algún elemento de apoyo para la movilidad o autonomía? | Silla de ruedas · Bastón · Andador · Muletas · Prótesis · No utilizo | ✅ |
| B4 | single_choice | B1=Sí | ¿Tu discapacidad afecta tu desempeño o comodidad en el trabajo? | Sí, significativamente · Sí, aspectos puntuales · No, bien gestionada · Prefiero no decir | ✅ | | 1.4 | single_choice | 1.1=Sí | ¿Contás con certificado oficial de discapacidad? | Sí · No, pero estoy en trámite · No | ✅ |
| B5 | single_choice | B1=Sí | ¿La empresa conoce tu situación? | Sí, con apoyo · Sí, sin apoyo · No, privado | ✅ | | 1.5 | single_choice | 1.1=Sí | ¿Tu condición afecta tu desempeño o comodidad en el trabajo? | Sí, de forma significativa · Sí, pero en aspectos puntuales · No, está bien gestionada · Prefiero no decir | ✅ |
| 1.6 | single_choice | trigger | ¿Tenés algún familiar cercano (hijo/a, pareja, hermano/a, padre/madre) que viva con discapacidad?* | Sí · No · Prefiero no decir | ✅ |
| 1.7 | multiple_choice | 1.6=Sí | ¿Cuál es tu vínculo con esa persona? | Hijo/a · Cónyuge/pareja · Padre o madre · Hermano/a · Otro familiar | ✅ |
| 1.8 | multiple_choice | 1.6=Sí | ¿Qué tipo de discapacidad tiene? | Física/motriz · Visual · Auditiva · Intelectual · TEA · Psicosocial/salud mental · Condición crónica severa · Otra | ✅ |
| 1.9 | multiple_choice | 1.6=Sí | ¿Qué tipo de apoyo o acompañamiento necesita principalmente? | Movilidad física · Comunicación · Apoyo escolar/educativo · Inclusión laboral · Terapias/rehabilitación · Apoyo emocional/psicosocial | ✅ |
| 1.10 | multiple_choice | 1.6=Sí | ¿Usa algún elemento de apoyo para la movilidad o autonomía? | Silla de ruedas · Bastón · Andador · Muletas · Prótesis · No utiliza | ✅ |
| 1.11 | single_choice | 1.6=Sí | Si es niño/a o adolescente en edad escolar: ¿está escolarizado/a y enfrenta barreras de aprendizaje? | Escolarizado/a, sin barreras importantes · Escolarizado/a, enfrenta barreras o faltan ajustes · No está escolarizado/a · No aplica | ✅ |
| 1.12 | single_choice | 1.6=Sí | ¿La persona con discapacidad se encuentra trabajando actualmente? | Sí, está trabajando · No, y está buscando empleo · No, y no está buscando · No aplica | ✅ |
| 1.13 | single_choice | trigger | ¿Tenés algún adulto mayor (60+ años) a tu cuidado o bajo tu responsabilidad?* | Sí · No · Prefiero no decir | |
| 1.14 | single_choice | 1.13=Sí | ¿Qué nivel de dependencia tiene? (la más representativa) | Independiente, pero necesita supervisión · Dependencia leve · Dependencia moderada · Dependencia severa | ✅ |
| 1.15 | multiple_choice | 1.13=Sí | ¿Tiene una condición de salud que genere mayor necesidad de cuidado? | Demencia/Alzheimer · ACV/Parálisis · Enfermedad crónica grave · Movilidad reducida · Deterioro cognitivo · Sin condición específica | ✅ |
| 1.16 | single_choice | 1.13=Sí | ¿Vive con vos o en otra vivienda? | Vive en mi hogar · Vive en otra vivienda · Está en residencia/institución | |
## Sección 2 — Familiares con Discapacidad *(sensible)* ## Sección 2 — Intensidad y rol del cuidado (top-level)
| Code | Tipo | Cond. | Pregunta | Opciones | is_sensitive |
|---|---|---|---|---|---|
| C1 | single_choice | trigger | ¿Tenés algún familiar cercano que viva con discapacidad? | Sí · No · Prefiero no decir | ✅ |
| C2 | multiple_choice | C1=Sí | ¿Cuál es el vínculo? (más de una) | Hijo/a · Cónyuge/pareja · Padre/madre · Hermano/a · Otro familiar | ✅ |
| C3 | multiple_choice | C1=Sí | ¿Qué tipo de discapacidad tiene? (más de una) | Física/motriz · Visual · Auditiva · Intelectual · TEA · Psicosocial · Condición crónica | ✅ |
| C4 | single_choice | C1=Sí | ¿Esta persona requiere apoyo o cuidado cotidiano? | Alta dependencia · Apoyo parcial · Autónomo/a | ✅ |
| C5 | single_choice | C1=Sí | ¿Quién es el responsable principal del cuidado? | Yo principal · Compartido · Cuidador pagado · Institución | |
## Sección 3 — Adultos Mayores a Cargo *(sensible)*
| Code | Tipo | Cond. | Pregunta | Opciones | is_sensitive |
|---|---|---|---|---|---|
| D1 | single_choice | trigger | ¿Tenés algún adulto mayor (60+) a tu cuidado? | Sí · No · Prefiero no decir | |
| D2 | single_choice | D1=Sí | ¿Cuántos adultos mayores están bajo tu cuidado? | 1 · 2 · 3+ | |
| D3 | single_choice | D1=Sí | ¿Qué nivel de dependencia tiene/n? | Supervisión · Dependencia leve · Dependencia moderada · Dependencia severa | ✅ |
| D4 | multiple_choice | D1=Sí | ¿Tiene condición diagnosticada? (más de una) | Demencia/Alzheimer · ACV/Parálisis · Enf. crónica · Movilidad reducida · Deterioro cognitivo · Ninguna específica | ✅ |
| D5 | single_choice | D1=Sí | ¿La persona vive con vos o en otra vivienda? | Vive conmigo · Otra vivienda · Institución | |
## Sección 4 — Tu Rol como Cuidador/a (top-level)
| Code | Tipo | Pregunta | Opciones | | Code | Tipo | Pregunta | Opciones |
|---|---|---|---| |---|---|---|---|
| E1 | single_choice | ¿Cuántas horas por día dedicás, en promedio, al cuidado directo? | Menos de 1h · 1-3h · 3-6h · Más de 6h · Variable | | 2.1 | single_choice | ¿Quién es el/la responsable principal del cuidado? | Yo soy el/la principal · Comparto con otro familiar · Hay cuidador/a pagado/a · Está en institución/centro de día |
| E2 | multiple_choice | ¿En qué momentos realizás principalmente el cuidado? (más de una) | Antes del trabajo · Durante horario laboral · Al llegar · Durante la noche · Fines de semana · Continuo | | 2.2 | single_choice | ¿Cuántas horas por día dedicás, en promedio, al cuidado directo? | Menos de 1h · Entre 1 y 3h · Entre 3 y 6h · Más de 6h · Varía mucho |
| E3 | single_choice | ¿Contás con apoyo de otras personas para el cuidado? | Apoyo familiar suficiente · Apoyo insuficiente · Único responsable · Apoyo contratado | | 2.3 | multiple_choice | ¿En qué momentos del día cuidás principalmente? | Antes del trabajo · Durante horario laboral (llamadas/emergencias) · Al llegar del trabajo · Durante la noche · Los fines de semana · Es continuo sin horario fijo |
| E4 | single_choice | ¿Tuviste que faltar al trabajo por el cuidado en el último año? | Nunca · Pocas veces (1-3) · Varias veces (4-10) · Con frecuencia (10+) | | 2.4 | single_choice | ¿Contás con apoyo de otras personas para el cuidado? | Sí, apoyo familiar suficiente · Algo de apoyo, no suficiente · No, soy el/la único/a responsable · Tengo apoyo contratado |
| 2.5 | single_choice | Como cuidador/a primario, ¿presentás signos de agotamiento o estrés crónico? | Sí, frecuentemente · A veces · No, lo llevo bien · Prefiero no decir |
## Sección 5 — Impacto Económico (top-level) ## Sección 3 — Impacto económico del cuidado (top-level)
| Code | Tipo | Pregunta | Opciones | Notas |
|---|---|---|---|---|
| 3.1 | single_choice | ¿Cuánto estimás que gastás por mes en el cuidado? | Sin gastos significativos · Menos de Gs. 500.000 · Gs. 500.0001.500.000 · Gs. 1.500.0003.000.000 · Gs. 3.000.0006.000.000 · Más de Gs. 6.000.000 · Prefiero no decir | |
| 3.2 | multiple_choice | ¿Qué servicios o apoyos utilizás actualmente? | Terapias especializadas · Cuidador/a particular · Centro de día/institución · Transporte adaptado · Medicación alto costo · Equipamiento médico/tecnología asistiva · No utilizo servicios formales | |
| 3.3 | multiple_choice | ¿Cómo sostenés los gastos y responsabilidades del cuidado? | Con mi empleo de tiempo completo · Con mi empleo de tiempo parcial · Con trabajo independiente/emprendimiento · Me ayudan mis familiares · Es difícil sostenerlo con mis ingresos | |
| 3.4 | single_choice | ¿En qué franja salarial mensual te ubicás? *(CONFIDENCIAL — analyst-only)* | Sueldo mínimo Gs. 5.000.000 · Gs. 5.000.0018.000.000 · Gs. 8.000.00112.000.000 · Gs. 12.000.00115.000.000 · Gs. 15.000.00118.000.000 · Gs. 18.000.00125.000.000 · Gs. 25.000.00150.000.000 · Gs. 50.000.001 o más · Prefiero no decir | is_sensitive=true; never in org_admin views |
| 3.5 | rating | ¿Los gastos de cuidado impactan en tu situación económica personal? | 1=Sin impacto · 2=Leve · 3=Moderado · 4=Alto · 5=Muy alto | RE-INDEX |
## Sección 4 — Tiempo y descanso (top-level)
| Code | Tipo | Pregunta | Opciones | | Code | Tipo | Pregunta | Opciones |
|---|---|---|---| |---|---|---|---|
| F1 | single_choice | ¿Cuánto estimás que gastás mensualmente en el cuidado? | Sin gastos significativos · Menos de 500k · 500k-1.5M · 1.5M-3M · 3M-6M · Más de 6M · Prefiero no decir | | 4.1 | single_choice | ¿Cómo describirías tu nivel de descanso habitual? | Descanso bien, me siento recuperado/a · Descanso de forma irregular, pero lo manejo · Me cuesta descansar, me siento cansado/a con frecuencia · Descanso muy poco, el agotamiento afecta mi día a día | RE-INDEX |
| F2 | multiple_choice | ¿Qué servicios o apoyos utilizás actualmente? (más de una) | Terapias especializadas · Cuidador particular · Centro de día · Transporte adaptado · Medicación alto costo · Tecnología asistiva · Apoyo ONG · Sin servicios formales | | 4.2 | multiple_choice | ¿Qué aspectos de tu vida se ven más afectados por tu rol de cuidador/a? | Tiempo libre y ocio · Sueño y descanso · Vida social y amistades · Vida en pareja/familiar · Salud física personal · Salud emocional/mental · Desarrollo profesional · Situación económica | |
| F3 | rating | ¿Los gastos de cuidado impactan en tu situación económica personal? | Escala 1 (Sin impacto) → 5 (Muy alto) | | 4.3 | rating | En una escala del 1 al 5, ¿cómo evaluás tu bienestar general en este momento? | 1=Muy mal · 2=Mal · 3=Regular · 4=Bien · 5=Muy bien | RE-INDEX |
## Sección 6Bienestar y Descanso (top-level) ## Sección 5Impacto en el trabajo (top-level)
| Code | Tipo | Pregunta | Opciones | | Code | Tipo | Pregunta | Opciones |
|---|---|---|---| |---|---|---|---|
| G1 | single_choice | ¿Cómo describirías tu nivel de descanso habitual? | Descanso bien · Descanso irregular · Me cuesta descansar · Muy poco descanso | | 5.1 | single_choice | En los últimos 3 meses, ¿con qué frecuencia sentiste que el cuidado afectó tu concentración o rendimiento? | Nunca o casi nunca · Algunas veces al mes · Varias veces por semana · Casi todos los días | RE-INDEX |
| G2 | single_choice | En los últimos 3 meses, ¿con qué frecuencia el cuidado afectó tu concentración o rendimiento laboral? | Nunca/casi nunca · Algunas veces al mes · Varias veces por semana · Casi todos los días | | 5.2 | single_choice | En el último año, ¿faltaste al trabajo por situaciones relacionadas al cuidado? | Nunca · Pocas veces (13 veces al año) · Varias veces (410 veces al año) · Con frecuencia (más de 10 veces al año) | |
| G3 | multiple_choice | ¿Qué aspectos de tu vida se ven más afectados? (más de una) | Tiempo libre · Sueño y descanso · Vida social · Vida en pareja/familia · Salud física · Salud emocional · Desarrollo profesional · Situación económica |
| G4 | rating | ¿Cómo evaluás tu bienestar general en este momento? | Escala 1 (Muy mal) → 5 (Muy bien) |
| G5 | single_choice | ¿Sentís que tu empresa comprende la realidad de ser cuidador/a? | Sí, acompañado/a · Parcialmente · No, invisible · Nunca lo supo |
## Sección 7Necesidades (top-level) ## Sección 6Conciencia organizacional (top-level)
| Code | Tipo | Pregunta | Opciones / nota | | Code | Tipo | Pregunta | Opciones | Notas |
|---|---|---|---|---|
| 6.1 | single_choice | ¿Sentís que tu empresa conoce o comprende la realidad del cuidado y la discapacidad de su gente?* | Sí, me siento acompañado/a · Parcialmente, hay cierta comprensión · No, es algo bastante invisible · No, nunca se ha hablado del tema | RE-INDEX |
| 6.2 | multiple_choice (**max_select=3**) | ¿Qué apoyos de la empresa te ayudarían más? Elegí hasta 3 | Flexibilidad horaria o trabajo remoto · Apoyo económico o convenios/descuentos · Acceso a terapias o salud · Contención/apoyo emocional o psicológico · Capacitación y autocuidado · Redes de apoyo/grupos de pares · Información sobre recursos y servicios disponibles | ⚠️ Ranking → multiple_choice para v1. Ranking widget al backlog. |
| 6.3 | single_choice | ¿Estarías dispuesto/a a participar en un grupo de apoyo para cuidadores dentro de la empresa? | Sí, definitivamente · Tal vez, si hay más personas en la misma situación · No por ahora · Prefiero mantenerlo en privado | |
| 6.4 | single_choice | ¿Conocés a algún compañero/a de trabajo que esté al cuidado de una persona con discapacidad o dependiente? | Sí, conozco a varias personas · Sí, conozco a una persona · No conozco a nadie · No estoy seguro/a | Código corregido (era 6.2 duplicado) |
| 6.5 | single_choice | ¿Considerás que la empresa debería ofrecer apoyos a quienes cuidan a una persona dependiente? | Sí, definitivamente · Tal vez, dependería del caso · No lo considero necesario · No lo sé | Código corregido (era 6.3 duplicado) |
## Sección 7 — Para cerrar (top-level)
| Code | Tipo | Pregunta | Notas |
|---|---|---|---| |---|---|---|---|
| H1 | multiple_choice (**max_select=3**) | ¿Qué tipo de apoyo de tu empresa te ayudaría más? (hasta 3) | Flexibilidad horaria/remoto · Info recursos y servicios · Apoyo psicológico · Espacio de escucha · Convenios descuentos · Grupos de pares · Capacitación autocuidado · Nada, estoy bien | | 7.1 | text_long | ¿Te gustaría dejar un comentario o sugerencia especial? (opcional) | No entra al store analítico crudo (regla 4 CLAUDE.md) |
| H2 | single_choice | ¿Estarías dispuesto/a a participar en un grupo de apoyo para cuidadores? | Sí, definitivamente · Tal vez · No por ahora · Prefiero privacidad |
| H3 | text_long | ¿Querés compartir algo más sobre tu experiencia? (opcional) | Texto libre, opcional. **No entra al store analítico crudo** (regla 4 CLAUDE.md). |
--- ---
## Mapeo a cuasi-identificadores (`responses.qi_*`) ## Mapeo a cuasi-identificadores (`responses.qi_*`)
- `qi_age_bucket`**A2** (ya viene en bucket — bien). - `qi_age_bucket`A5 (ya en bucket)
- `qi_sector`**A1** (área). - `qi_sector`A4 (área)
- `qi_zone`**no aplica en este instrumento** (no se pregunta zona geográfica; es intra-empresa). Va NULL. - `qi_zone`NULL (no aplica en este instrumento)
- `qi_city` / `qi_country` ← A2/A1: **Sprint 3** — se agregan como columnas qi en migration futura.
Por ahora A1/A2/A3 se capturan en `answers` como text_short.
## ⚠️ Hallazgo para el director (decisión de modelo de datos, Nivel 3 — NO ejecutar) ## ⚠️ Decisiones pendientes para el director (NO ejecutar sin aprobación)
El dashboard original quería filtrar por **género (A3)** y **modalidad (A4)**, pero `agg_segment` - **Ranking widget (6.2):** actual es multiple_choice max_select=3. Cuando se implemente el
solo tiene en su whitelist `qi_age_bucket / qi_sector / qi_zone`. Para segmentar/filtrar por género o widget de ranking (drag o 3 dropdowns), 6.2 pasa a tipo `ranking` con JSONB `{rank1, rank2, rank3}`.
modalidad en el dashboard (Sprint 3), hay que decidir: ¿se agregan columnas `qi_gender` / `qi_modality` - **qi_city / qi_country:** migration 008 para agregar columnas y extender whitelist de agg_segment.
(y a la whitelist de `agg_segment`), o se resuelven por otra vía? Es decisión de esquema → consultar Se hace antes de Sprint 3 (dashboard).
antes de Sprint 3, no resolver en el seed. - **Salary analytics:** metodología para calcular "carga económica del cuidado como % del ingreso"
es trabajo de metodología de investigación (fuera del scope del código).
- **RE-INDEX scoring:** algoritmo de composición de los índices (descanso, bienestar, rendimiento,
conciencia) — decisión antes de construir los reportes del dashboard.
- **Formulario de empresa (Relevamiento Empresa):** instrumento separado con PII de contacto.
Ejecutar offline para las primeras empresas. Diseño del modelo de datos en el sprint del builder.

View File

@@ -254,6 +254,26 @@ body {
.rating-step:hover .rating-dot { border-color: var(--color-accent); background: var(--color-primary-light); } .rating-step:hover .rating-dot { border-color: var(--color-accent); background: var(--color-primary-light); }
.rating-step:has(input:checked):hover .rating-dot { background: var(--color-primary-hover); border-color: var(--color-primary-hover); color: #fff; } .rating-step:has(input:checked):hover .rating-dot { background: var(--color-primary-hover); border-color: var(--color-primary-hover); color: #fff; }
/* ── Text short (single-line input) ── */
.text-short-input {
display: block;
width: 100%;
padding: 10px 12px;
border: 1.5px solid var(--color-border);
border-radius: var(--radius);
font-size: 1rem;
font-family: inherit;
line-height: 1.5;
transition: border-color 0.15s;
color: var(--color-text);
background: var(--color-surface);
}
.text-short-input:focus {
outline: none;
border-color: var(--color-border-focus);
box-shadow: 0 0 0 3px rgba(37,99,235,0.15);
}
/* ── Textarea ── */ /* ── Textarea ── */
.text-area { .text-area {
display: block; display: block;

View File

@@ -4,6 +4,7 @@ import { RadioGroup } from './widgets/radio-group'
import { CheckboxGroup } from './widgets/checkbox-group' import { CheckboxGroup } from './widgets/checkbox-group'
import { RatingScale } from './widgets/rating-scale' import { RatingScale } from './widgets/rating-scale'
import { TextAreaField } from './widgets/text-area-field' import { TextAreaField } from './widgets/text-area-field'
import { TextShortField } from './widgets/text-short-field'
interface QuestionRendererProps { interface QuestionRendererProps {
question: Question question: Question
@@ -72,7 +73,20 @@ export function QuestionRenderer({
) )
} }
if (type === 'text_long' || type === 'text_short') { if (type === 'text_short') {
return (
<TextShortField
name={code}
label={prompt}
value={(answers[code] as string) ?? ''}
required={required}
onChange={(v) => onChange(code, v)}
error={error}
/>
)
}
if (type === 'text_long') {
return ( return (
<TextAreaField <TextAreaField
name={code} name={code}

View File

@@ -35,16 +35,22 @@ function formatAnswer(question: Question, raw: FormAnswers[string]): string {
return String(raw) return String(raw)
} }
// Section titles by first question code prefix // Section titles by code prefix ('A', '1', '2', …)
const SECTION_TITLES: Record<string, string> = { const SECTION_TITLES: Record<string, string> = {
A: 'Datos Generales', A: 'Datos Generales',
B: '¿Vivís con discapacidad?', '1': 'Prevalencia: cuidado y discapacidad',
C: 'Familiares con Discapacidad', '2': 'Intensidad y rol del cuidado',
D: 'Adultos Mayores a Cargo', '3': 'Impacto económico del cuidado',
E: 'Tu Rol como Cuidador/a', '4': 'Tiempo y descanso',
F: 'Impacto Económico', '5': 'Impacto en el trabajo',
G: 'Bienestar y Descanso', '6': 'Conciencia organizacional',
H: 'Necesidades', '7': 'Para cerrar',
}
// 'A1' → 'A', '1.1' → '1', '6.4' → '6', '7.1' → '7'
function getSectionPrefix(code: string): string {
const m = code.match(/^([A-Za-z]+|\d+)/)
return m ? m[1] : code
} }
export function SummaryScreen({ export function SummaryScreen({
@@ -72,7 +78,7 @@ export function SummaryScreen({
<dl className="summary-list"> <dl className="summary-list">
{visibleQuestions.map((q) => { {visibleQuestions.map((q) => {
const section = q.code.replace(/\d+$/, '') const section = getSectionPrefix(q.code)
const showHeader = section !== lastSection const showHeader = section !== lastSection
lastSection = section lastSection = section

View File

@@ -8,19 +8,23 @@ import { QuestionRenderer } from './question-renderer'
import { SummaryScreen } from './summary-screen' import { SummaryScreen } from './summary-screen'
// Section metadata (code prefix → display title) // Section metadata (code prefix → display title)
// Prefixes: 'A' for A1-A7, '1' for 1.1-1.16, '2' for 2.1-2.5, etc.
const SECTIONS: { prefix: string; title: string; sensitive?: boolean }[] = [ const SECTIONS: { prefix: string; title: string; sensitive?: boolean }[] = [
{ prefix: 'A', title: 'Datos Generales' }, { prefix: 'A', title: 'Datos Generales' },
{ prefix: 'B', title: '¿Vivís con discapacidad?', sensitive: true }, { prefix: '1', title: 'Prevalencia: cuidado y discapacidad', sensitive: true },
{ prefix: 'C', title: 'Familiares con Discapacidad', sensitive: true }, { prefix: '2', title: 'Intensidad y rol del cuidado' },
{ prefix: 'D', title: 'Adultos Mayores a Cargo', sensitive: true }, { prefix: '3', title: 'Impacto económico del cuidado' },
{ prefix: 'E', title: 'Tu Rol como Cuidador/a' }, { prefix: '4', title: 'Tiempo y descanso' },
{ prefix: 'F', title: 'Impacto Económico' }, { prefix: '5', title: 'Impacto en el trabajo' },
{ prefix: 'G', title: 'Bienestar y Descanso' }, { prefix: '6', title: 'Conciencia organizacional' },
{ prefix: 'H', title: 'Necesidades' }, { prefix: '7', title: 'Para cerrar' },
] ]
function getSectionPrefix(code: string) { // Extracts the leading alpha or numeric segment from a question code.
return code.replace(/\d+$/, '') // 'A1' → 'A', '1.1' → '1', '1.16' → '1', '6.4' → '6', '7.1' → '7'
function getSectionPrefix(code: string): string {
const m = code.match(/^([A-Za-z]+|\d+)/)
return m ? m[1] : code
} }
interface SurveyFormProps { interface SurveyFormProps {

View File

@@ -0,0 +1,50 @@
'use client'
interface TextShortFieldProps {
name: string
label: string
value: string
required: boolean
placeholder?: string
onChange: (value: string) => void
error?: string
}
export function TextShortField({
name,
label,
value,
required,
placeholder = '',
onChange,
error,
}: TextShortFieldProps) {
return (
<div className="question-fieldset">
<label className="question-legend" htmlFor={name}>
{label}
{required && <span className="required-mark" aria-hidden="true"> *</span>}
{required && <span className="sr-only"> (obligatorio)</span>}
</label>
<input
type="text"
id={name}
name={name}
value={value}
required={required}
aria-required={required}
aria-describedby={error ? `${name}-error` : undefined}
aria-invalid={!!error}
placeholder={placeholder}
onChange={(e) => onChange(e.target.value)}
className="text-short-input"
autoComplete="off"
/>
{error && (
<p role="alert" className="field-error" id={`${name}-error`}>
{error}
</p>
)}
</div>
)
}

View File

@@ -84,6 +84,19 @@ compartidos; la inspección fila-por-fila es solo análisis interno (Modo A).
Las preguntas abiertas no se exponen crudas a reportes (vector de re-identificación). Se categorizan Las preguntas abiertas no se exponen crudas a reportes (vector de re-identificación). Se categorizan
antes (a futuro, vía LLM — está en BACKLOG). antes (a futuro, vía LLM — está en BACKLOG).
### 11. Salary range — dato de acceso analyst-only (addendum Jun 2026)
La pregunta `3.4` captura la franja salarial del respondente. Es el dato clave para calcular la
"carga económica del cuidado como % del ingreso" — núcleo del argumento de la economía del cuidado.
Decisión del equipo:
- Se recolecta y almacena en `answers.value` como cualquier respuesta. `is_sensitive=true`.
- Acceso: **exclusivamente rol analyst** (vía `service_role`, que bypassa RLS). Nunca expuesto
a org_admin — ni en vistas crudas ni en funciones de agregación accesibles a ese rol.
- Ninguna llamada a `agg_segment` ni ningún reporte de org_admin incluye salary como dimensión
o como valor. El GRANT existente ya lo bloquea (org_admin tiene 0 filas en `answers`).
- Protección "técnicamente casi imposible": RLS bloquea el path de org_admin + cifrado en reposo
de Supabase + audit logs de accesos. Riesgo de breach externo es categoría separada
(plan de respuesta a incidentes, fuera del scope de este ADR).
## Alcance de esta etapa (v1) ## Alcance de esta etapa (v1)
Solo **Modo A**, solo encuestas **anónimas**, **sin Nivel 3 atribuible**. El esquema soporta más Solo **Modo A**, solo encuestas **anónimas**, **sin Nivel 3 atribuible**. El esquema soporta más
(controller_role, respondent_id) pero v1 no lo opera. Principio: el esquema guarda la opción, estos (controller_role, respondent_id) pero v1 no lo opera. Principio: el esquema guarda la opción, estos
@@ -107,4 +120,4 @@ datos puntuales no la usan.
costo correcto de la privacidad. costo correcto de la privacidad.
## Pendiente ## Pendiente
Validación del flujo de consentimiento y del lenguaje legal con asesor paraguayo antes de producción. Validación del flujo de consentimiento y del lenguaje legal con asesor paraguayo antes de producción.

View File

@@ -0,0 +1,75 @@
# ETAPA 1D — Actualización del instrumento a v2 (44 preguntas)
> Implementa la revisión del instrumento hecha por la colega del equipo.
> Leé: `CLAUDE.md`, `DATA_MODEL.md`, `SEED_SPEC.md` (v2) y este prompt.
## 1. Objetivo
Actualizar el seed y la app para que reflejen el instrumento v2 de 44 preguntas según `SEED_SPEC.md`.
Dos partes: (A) base de datos en Supabase Cloud, (B) widget `text_short` en el formulario Next.js.
## 2. Contexto previo
La Etapa 1C cerró la fundación de datos con 34 preguntas. Esta etapa la actualiza a 44 por decisión
del equipo. El esquema de tablas NO cambia — solo cambia el contenido del seed. Las columnas `qi_city`
y `qi_country` se agregan en un sprint futuro (no en esta etapa).
## 3. Decisiones ya tomadas
- 44 preguntas con códigos únicos según `SEED_SPEC.md`. Códigos corregidos: 6.4 y 6.5 (las
que en el HTML original eran 6.2 y 6.3 duplicados).
- A1/A2/A3 son tipo `text_short` (texto libre corto), sin opciones. Se guardan en `answers`.
- Pregunta 6.2 = `multiple_choice max_select=3` (NO ranking todavía).
- Pregunta 3.4 (salary) = `is_sensitive=true`. Ninguna vista de org_admin la expone.
- Condicionales: cond-disc-propia (1.1=Sí → 1.21.5), cond-familiar (1.6=Sí → 1.71.12),
cond-adulto-mayor (1.13=Sí → 1.141.16). Reglas en la pregunta dependiente.
- Secciones A, 2, 3, 4, 5, 6, 7 son top-level (siempre visibles).
- RE-INDEX es solo una nota en los textos de `SEED_SPEC.md`; NO agrega columna al esquema.
## 4. Tareas
### 4A — Base de datos
1. Reescribir `supabase/seed.sql` con las 44 preguntas de `SEED_SPEC.md`: tipos, opciones,
`max_select` (6.2=3), `is_sensitive` (marcar las indicadas ✅), `conditional_rules` en
dependientes, `required=false` para todas (el equipo decidió capturar todas las respuestas
para validar el concepto, la obligatoriedad real se define luego).
2. Aplicar a Cloud: `supabase db push --include-seed` o equivalente.
3. Verificar: `SELECT count(*) FROM questions` = **44**; códigos únicos sin duplicados;
pregunta `3.4` con `is_sensitive=true`; los 3 bloques condicionales tienen las reglas correctas.
### 4B — App (formulario Next.js)
4. En `components/widgets/`, crear `text-short-field.tsx`: un `<input type="text">` accesible
(label asociado, aria-required, aria-invalid) con el mismo estilo visual que los otros widgets.
5. En `components/question-renderer.tsx`: registrar `text_short` y renderizarlo con el nuevo widget.
6. Verificar que A1/A2/A3 se renderizan como campos de texto, y que el formulario sigue
funcionando end-to-end (cargar las 44 preguntas desde Supabase, condicionales B/C/D, envío
al resumen).
## 5. Definition of Done
- [ ] `SELECT count(*) FROM questions` = 44 contra Cloud.
- [ ] Códigos únicos: `SELECT code, count(*) FROM questions GROUP BY code HAVING count(*) > 1` = vacío.
- [ ] `3.4` tiene `is_sensitive=true`.
- [ ] Pregunta `A1` con `type='text_short'` y `options=null`.
- [ ] `npm run build` limpio.
- [ ] El formulario en `http://localhost:3000` muestra A1/A2/A3 como campos de texto.
- [ ] B1=Sí despliega 1.21.5; C1=Sí despliega 1.71.12; D1=Sí despliega 1.141.16.
- [ ] `git diff --name-only HEAD` solo con `supabase/seed.sql` + archivos de la app.
## 6. Validaciones automatizadas mandatorias
- Conteo de preguntas: 44 ✓
- Sin códigos duplicados (query arriba) ✓
- `is_sensitive` en exactamente las preguntas marcadas en SEED_SPEC.md ✓
- Al menos una conditional_rule en cada una de las 16 preguntas dependientes ✓
- `npm run build` sin errores ✓
- Si CUALQUIERA falla: NO declarar lista. Reportar y esperar.
## 7. Qué reportar
- Conteo de preguntas y confirmación de código únicos.
- Confirmación de que A1/A2 se renderizan correctamente en el formulario.
- Salida de `git diff --name-only HEAD`.
- Hallazgos, asumidos, decisiones por iniciativa (con nivel).
## 8. Qué NO hacer
- NO agregar columnas `qi_city`/`qi_country` al esquema (es Sprint 3).
- NO implementar widget de ranking (está en backlog).
- NO tocar migraciones 001007.
- NO cambiar la lógica de RLS ni los policies.
- NO exponer `3.4` en ninguna vista o función que sea accesible a org_admin.
- Ante conflicto restricción vs build limpio: **detenerse y reportar**.

View File

@@ -17,8 +17,9 @@ distintos y conviene auditarlos por separado:
## Alcance ## Alcance
- **Etapa 2A — Motor del formulario:** lee la encuesta live desde Supabase (anon key), renderiza las - **Etapa 2A — Motor del formulario:** lee la encuesta live desde Supabase (anon key), renderiza las
34 preguntas con el widget correcto por tipo, ejecuta la lógica condicional, valida, mobile-first, **44 preguntas** (instrumento v2, Jun 2026) con el widget correcto por tipo (`single_choice`,
WCAG AA, resiliencia lite. **Al enviar muestra un resumen; NO escribe en la base.** `multiple_choice`, `rating`, `text_long`, `text_short`), ejecuta la lógica condicional, valida,
mobile-first, WCAG AA, resiliencia lite. **Al enviar muestra un resumen; NO escribe en la base.**
- **Etapa 2B — Consentimiento + persistencia:** pantalla de consentimiento granular versionado - **Etapa 2B — Consentimiento + persistencia:** pantalla de consentimiento granular versionado
(ADR-003) con texto honesto sobre anonimato y no-revocación, que crea el `consent_record` y recién (ADR-003) con texto honesto sobre anonimato y no-revocación, que crea el `consent_record` y recién
ahí escribe `response` + `answers`, con dedup por `ip_hash`. ahí escribe `response` + `answers`, con dedup por `ip_hash`.
@@ -34,15 +35,17 @@ distintos y conviene auditarlos por separado:
- El formulario corre como **`anon`** (sin login). `service_role` NUNCA en el cliente (regla 9). - El formulario corre como **`anon`** (sin login). `service_role` NUNCA en el cliente (regla 9).
- El respondente nunca se ata a un IdP (regla 10 / ADR-004). - El respondente nunca se ata a un IdP (regla 10 / ADR-004).
- `options`: array para `single_choice`/`multiple_choice`, objeto `{min,max,min_label,max_label}` para `rating`. - `options`: array para `single_choice`/`multiple_choice`, objeto `{min,max,min_label,max_label}` para `rating`.
- Condicionales en la pregunta dependiente; disparador value `'Sí'` (B1/C1/D1). - Condicionales en la pregunta dependiente; disparador value `'Sí'` (triggers: 1.1/1.6/1.13).
- Tipos de widget: `single_choice`→radio, `multiple_choice`→checkbox, `rating`→escala 1-5,
`text_long`→textarea, `text_short`→input de texto de una línea (A1/A2/A3).
- WCAG 2.2 AA es pilar (el sujeto es la discapacidad), no checkbox. - WCAG 2.2 AA es pilar (el sujeto es la discapacidad), no checkbox.
- El mockup `formulario_mapeo.html` es el **contrato visual** (look y flujo ya aprobados); 2A lo - El mockup `formulario_mapeo.html` es el **contrato visual** (look y flujo ya aprobados); 2A lo
replica pero data-driven desde el esquema, no hardcodeado. replica pero data-driven desde el esquema, no hardcodeado.
## Criterios de éxito (Etapa 2A) ## Criterios de éxito (Etapa 2A)
- [ ] Renderiza las 34 preguntas leídas desde Supabase (no hardcodeadas). - [ ] Renderiza las **44 preguntas** leídas desde Supabase (no hardcodeadas).
- [ ] Lógica condicional: B1/C1/D1 = "Sí" despliega sus bloques; "No"/"Prefiero no decir" los oculta y limpia. - [ ] Lógica condicional: 1.1/1.6/1.13 = "Sí" despliegan sus bloques; "No"/"Prefiero no decir" ocultan y limpian.
- [ ] H1 limita a 3 selecciones; `rating` (F3/G4) maneja el formato objeto. - [ ] 6.2 limita a 3 selecciones; `rating` (3.5/4.3) maneja formato objeto; A1/A2/A3 son `text_short`.
- [ ] Mobile-first fluido + navegable solo con teclado + WCAG AA base. - [ ] Mobile-first fluido + navegable solo con teclado + WCAG AA base.
- [ ] Refresh no pierde lo cargado (resiliencia lite). - [ ] Refresh no pierde lo cargado (resiliencia lite).
- [ ] Enviar muestra resumen; no escribe en la base. - [ ] Enviar muestra resumen; no escribe en la base.

View File

@@ -5,13 +5,13 @@
## 1. Objetivo ## 1. Objetivo
Construir el motor del formulario: una app Next.js que **lee** la encuesta live desde Supabase con la Construir el motor del formulario: una app Next.js que **lee** la encuesta live desde Supabase con la
`anon` key, renderiza las 34 preguntas con el widget correcto por tipo, ejecuta la lógica condicional, `anon` key, renderiza las **44 preguntas** (instrumento v2) con el widget correcto por tipo, ejecuta la lógica condicional,
valida, es mobile-first y accesible (WCAG AA), no pierde lo cargado al refrescar, y al enviar muestra valida, es mobile-first y accesible (WCAG AA), no pierde lo cargado al refrescar, y al enviar muestra
un **resumen de respuestas**. **NO escribe nada en la base** (eso es 2B). un **resumen de respuestas**. **NO escribe nada en la base** (eso es 2B).
## 2. Contexto previo ## 2. Contexto previo
La fundación de datos (Sprint 1) está cerrada y verificada en Cloud: esquema, RLS, seed real de 34 La fundación de datos (Sprint 1) está cerrada y verificada en Cloud: esquema, RLS, seed real de **44
preguntas, k-anonimato. El proyecto Supabase está linkeado. Todavía no existe app frontend — esta preguntas** (instrumento v2, Jun 2026), k-anonimato. El proyecto Supabase está linkeado. Todavía no existe app frontend — esta
etapa la scaffolding. `responses.consent_record_id` es `NOT NULL`, por eso 2A **no** persiste: la etapa la scaffolding. `responses.consent_record_id` es `NOT NULL`, por eso 2A **no** persiste: la
escritura requiere el consentimiento, que es 2B. El mockup `formulario_mapeo.html` (en el historial escritura requiere el consentimiento, que es 2B. El mockup `formulario_mapeo.html` (en el historial
del proyecto) es el contrato visual: replicar su look y flujo, pero leyendo del esquema, no hardcoded. del proyecto) es el contrato visual: replicar su look y flujo, pero leyendo del esquema, no hardcoded.
@@ -23,22 +23,25 @@ del proyecto) es el contrato visual: replicar su look y flujo, pero leyendo del
`NEXT_PUBLIC_SUPABASE_ANON_KEY` en `.env.local`. **El `service_role` NUNCA va a una variable `NEXT_PUBLIC_SUPABASE_ANON_KEY` en `.env.local`. **El `service_role` NUNCA va a una variable
`NEXT_PUBLIC_` ni al cliente** (regla 9 de CLAUDE.md). `NEXT_PUBLIC_` ni al cliente** (regla 9 de CLAUDE.md).
- `options`: **array** para `single_choice`/`multiple_choice`; **objeto** `{min,max,min_label,max_label}` - `options`: **array** para `single_choice`/`multiple_choice`; **objeto** `{min,max,min_label,max_label}`
para `rating` (F3, G4). Manejar las dos formas. para `rating` (3.5, 4.3); **null** para `text_short`/`text_long`. Manejar las tres formas.
- Tipos de widget: `single_choice`→radio, `multiple_choice`→checkbox, `rating`→escala 1-5,
`text_long`→textarea, `text_short``<input type="text">` de una línea (A1/A2/A3: País, Ciudad, Sede).
- Condicionales: cada pregunta dependiente trae `conditional_rules` con `{if_question, op:'eq', - Condicionales: cada pregunta dependiente trae `conditional_rules` con `{if_question, op:'eq',
value:'Sí', action:'show'}`. Mostrar solo si el disparador (B1/C1/D1) = "Sí"; con "No"/"Prefiero no value:'Sí', action:'show'}`. Triggers: **1.1** (→1.21.5), **1.6** (→1.71.12), **1.13** (→1.141.16).
decir" ocultar **y limpiar** las respuestas de ese bloque. Con "No"/"Prefiero no decir" ocultar **y limpiar** las respuestas del bloque.
- H1 = `multiple_choice` con `max_select=3` (impedir marcar más de 3). - 6.2 = `multiple_choice` con `max_select=3` (impedir marcar más de 3).
- 2A **no** escribe `responses`/`answers`/`consent_records`. Al enviar → pantalla de resumen. - 2A **no** escribe `responses`/`answers`/`consent_records`. Al enviar → pantalla de resumen.
## 4. Tareas ## 4. Tareas
1. Scaffolding de la app Next.js (App Router) en el repo. Setear el cliente Supabase con la `anon` 1. Scaffolding de la app Next.js (App Router) en el repo. Setear el cliente Supabase con la `anon`
key desde `.env.local`. Crear `.env.example` (sin valores) documentando las dos variables. key desde `.env.local`. Crear `.env.example` (sin valores) documentando las dos variables.
2. Leer desde Supabase (anon) la encuesta `status='live'` + sus 34 `questions` ordenadas por 2. Leer desde Supabase (anon) la encuesta `status='live'` + sus 44 `questions` ordenadas por
`position`, con sus `options`, `type`, `max_select`, `is_sensitive`, `conditional_rules`. `position`, con sus `options`, `type`, `max_select`, `is_sensitive`, `conditional_rules`.
(Si la encuesta seed no está `live`, dejar documentado que el director debe setearla a `live`, o (Si la encuesta seed no está `live`, dejar documentado que el director debe setearla a `live`, o
leer por id — NO cambiar el estado desde el cliente.) leer por id — NO cambiar el estado desde el cliente.)
3. Renderizar cada tipo con su widget: `single_choice`→radios, `multiple_choice`→checkboxes (con tope 3. Renderizar cada tipo con su widget: `single_choice`→radios, `multiple_choice`→checkboxes (con tope
`max_select`), `rating`→escala 1-5 (usando el objeto options), `text_long`→textarea. `max_select`), `rating`→escala 1-5 (usando el objeto options), `text_long`→textarea,
`text_short`→input de una línea (A1/A2/A3).
4. Motor de lógica condicional data-driven: evaluar `conditional_rules` para mostrar/ocultar; al 4. Motor de lógica condicional data-driven: evaluar `conditional_rules` para mostrar/ocultar; al
ocultar un bloque, limpiar sus valores. ocultar un bloque, limpiar sus valores.
5. Validación con zod (tipos correctos; respetar `max_select`; `required` según el campo `required` 5. Validación con zod (tipos correctos; respetar `max_select`; `required` según el campo `required`
@@ -54,17 +57,17 @@ del proyecto) es el contrato visual: replicar su look y flujo, pero leyendo del
## 5. Definition of Done ## 5. Definition of Done
- [ ] `npm run dev` levanta la app y carga la encuesta desde Supabase (anon). - [ ] `npm run dev` levanta la app y carga la encuesta desde Supabase (anon).
- [ ] Se renderizan las 34 preguntas; los 3 bloques condicionales aparecen/desaparecen con B1/C1/D1. - [ ] Se renderizan las **44 preguntas**; los 3 bloques condicionales aparecen/desaparecen con 1.1/1.6/1.13.
- [ ] H1 no deja marcar más de 3; rating funciona con el formato objeto. - [ ] 6.2 no deja marcar más de 3; rating (3.5/4.3) funciona con el formato objeto; A1/A2/A3 son inputs de texto.
- [ ] Navegable y operable solo con teclado; pasa una auditoría a11y básica (axe o Lighthouse) sin errores AA. - [ ] Navegable y operable solo con teclado; pasa una auditoría a11y básica (axe o Lighthouse) sin errores AA.
- [ ] Refresh conserva lo cargado; enviar limpia el almacenamiento y muestra el resumen. - [ ] Refresh conserva lo cargado; enviar limpia el almacenamiento y muestra el resumen.
- [ ] `npm run build` limpio; ninguna variable `NEXT_PUBLIC_` contiene el `service_role`. - [ ] `npm run build` limpio; ninguna variable `NEXT_PUBLIC_` contiene el `service_role`.
- [ ] `git diff --name-only HEAD` solo con archivos de la app nueva + `.env.example`. - [ ] `git diff --name-only HEAD` solo con archivos de la app nueva + `.env.example`.
## 6. Validaciones automatizadas mandatorias (antes de entregar) ## 6. Validaciones automatizadas mandatorias (antes de entregar)
- La carga lee **34** preguntas desde la base (logear el conteo), no un número hardcodeado. - La carga lee **44** preguntas desde la base (logear el conteo), no un número hardcodeado.
- Las 12 condicionales se evalúan desde `conditional_rules` (no con `if` cableados por código). - Las **13 condicionales** se evalúan desde `conditional_rules` (no con `if` cableados por código).
- `max_select` de H1 se respeta en runtime (test o verificación manual reproducible). - `max_select` de 6.2 se respeta en runtime (test o verificación manual reproducible).
- `npm run build` sin errores. - `npm run build` sin errores.
- Auditoría a11y (axe-core o `lighthouse`) sobre la página del formulario: **0 violaciones de nivel AA**. - Auditoría a11y (axe-core o `lighthouse`) sobre la página del formulario: **0 violaciones de nivel AA**.
- Grep de seguridad: `service_role` NO aparece en ningún archivo del cliente ni en `NEXT_PUBLIC_*`. - Grep de seguridad: `service_role` NO aparece en ningún archivo del cliente ni en `NEXT_PUBLIC_*`.

File diff suppressed because it is too large Load Diff