diff --git a/sprints/sprint-4/BRIEF.md b/sprints/sprint-4/BRIEF.md index 1f7e524..b1d3b17 100644 --- a/sprints/sprint-4/BRIEF.md +++ b/sprints/sprint-4/BRIEF.md @@ -1,96 +1,143 @@ -# BRIEF — Sprint 4: Formulario Wizard v3 +# BRIEF — Sprint 4: Formulario Wizard v3 (esquema declarativo) -> Versión objetivo: **3.0.0** · Rama de trabajo: `main` · Preservación: `v2.1.0-stable` +> Versión objetivo: **3.0.0** · Rama: `main` · Preservación: `v2.1.0-stable` --- ## Objetivo en una frase -Reescribir el formulario de encuesta como un wizard pregunta-por-pantalla con -mini-wizard anidado para vínculos familiares, sobre un nuevo instrumento de 10 -secciones, habilitando análisis de datos multi-instancia. +Construir el formulario de cuidadores como un wizard pregunta-por-pantalla cuyo +flujo, instancias y condiciones se definen en un **esquema declarativo** (datos), +renderizado por un frontend sin lógica de negocio — funcionando para los dos +clientes inminentes, y en la dirección arquitectónica del motor de formularios. + +--- + +## Decisión arquitectónica rectora (ADR pendiente de registrar) + +**El motor de formularios es el producto; la encuesta de cuidadores es su primer +caso de uso.** Por lo tanto, la lógica del flujo del formulario vive en una capa +de **definición declarativa** (datos), no en el presentador ni dispersa en columnas +sueltas de la base. + +### Estrategia en dos fases (deuda intencional, registrada — no oculta) + +**Fase 1 (este sprint, para el deadline de 10-20 días):** +Se construye el formulario de cuidadores con su flujo definido como esquema +declarativo persistido. El frontend es un renderer de ese esquema — sin reglas +hardcodeadas. La resolución de "qué pantalla sigue" se calcula desde esquema + +estado. NO se construye todavía el intérprete genérico universal. + +**Fase 2 (sprints posteriores, post-deadline):** +Se extrae el intérprete a un motor genérico de formularios. Como el formulario de +cuidadores ya está definido como esquema declarativo, se vuelve el primer caso de +prueba del motor sin reescribir el contrato. Registrado en `BACKLOG.md`. + +**Por qué esta estrategia:** entrega valor en plazo sin generar retrabajo. La +lógica en datos (no en presentación) es lo que habilita guardado parcial, +reanudación, validación consistente y progreso preciso — la mejor UX — y lo que +hace que la Fase 2 sea generalización, no reescritura. --- ## Alcance (qué entra) -- Nuevo modelo de datos: columna `instance_id` en `answers`, nueva constraint única -- Nuevo instrumento v3 (10 secciones, ~38 preguntas base + preguntas por instancia) -- Motor de wizard: grafo de pantallas, navegación, estado, instancias dinámicas -- Componente de pantalla única (una pregunta por pantalla) -- Mini-wizard anidado para vínculos familiares (pregunta 4.1 → instancias) -- Autocomplete de barrios desde dataset INE/DGEEC (`geo_paraguay.json`) +- Esquema declarativo del formulario de cuidadores (flujo, instancias, condiciones como datos) +- Persistencia del esquema (en seed/DB, versionado) +- `instance_id` en `answers` (Etapa 4A — ya aplicada) +- Motor de resolución de flujo para este formulario (interpreta el esquema) +- Frontend wizard como renderer del esquema (sin reglas de negocio) +- Mini-wizard de vínculos familiares (instancias declaradas en el esquema) +- Autocomplete de barrios desde dataset INE/DGEEC - Subtítulos del "porqué" por sección -- Submit actualizado para enviar `instance_id` por answer -- Barra de progreso dinámica -- RPCs nuevas para preguntas de vínculo con instancias +- Submit con `instance_id` por answer +- Barra de progreso dinámica (calculada desde el esquema + estado) +- RPCs para preguntas de vínculo con instancias - Seed v3 limpio (sin migración de datos v2) ---- - ## Fuera de alcance (explícito) -- ❌ Migración de datos v2.1 → v3 (se decide más adelante, cuando v3 esté estable) +- ❌ Motor genérico universal de formularios (Fase 2 — BACKLOG) +- ❌ Backend de construcción/edición de formularios (sprint posterior) +- ❌ Migración de datos v2.1 → v3 (se decide cuando v3 esté estable) - ❌ Tocar la rama `v2.1.0-stable` -- ❌ RE Index (sprint posterior) -- ❌ Informe descargable (sprint posterior) -- ❌ Autenticación de `/admin/responses` (TECH-DEBT-008) -- ❌ Backup remoto S3 (TECH-DEBT-006) - ---- +- ❌ RE Index, informe descargable, auth de /admin/responses, backup S3 ## Decisiones ya tomadas (no renegociar) -1. v3 se desarrolla en `main`. v2 queda preservado en `v2.1.0-stable` + tag `v2.1.0`. -2. v3 arranca con seed limpio — sin datos migrados. -3. `instance_id text DEFAULT 'default'` en `answers`. Constraint: - `UNIQUE (response_id, question_id, instance_id)`. -4. Wizard pregunta-por-pantalla para TODO el formulario tras el consentimiento. -5. Mini-wizard de familiares: una pregunta por pantalla, con badge de contexto - "Familiar X de N". -6. Numeración desde Sección 1 (no "Sección A"). -7. Barrio: autocomplete para ciudades con datos INE, texto libre para el resto. -8. Texto de consentimiento: el del Excel del instrumento v3 (Sección 1). -9. Versionado SemVer + CHANGELOG + tags de Git en cada versión estable. -10. 2-3 familiares es el caso típico; soportar N sin límite duro. - ---- +1. v3 en `main`; v2 preservado en `v2.1.0-stable` + tag `v2.1.0`. +2. Lógica de flujo en esquema declarativo (datos), NO en el frontend. +3. Frontend wizard = renderer sin reglas de negocio. +4. Estrategia dos fases: formulario-como-esquema ahora, motor genérico después. +5. v3 arranca con seed limpio, sin datos migrados. +6. `instance_id` ya implementado (4A). Instancias declaradas en el esquema. +7. Wizard pregunta-por-pantalla para todo el formulario tras consentimiento. +8. Mini-wizard de familiares: una pregunta por pantalla, badge "Familiar X de N". +9. Numeración desde Sección 1. +10. Barrio: autocomplete con datos INE, texto libre para el resto. +11. Texto de consentimiento: el del Excel del instrumento v3. +12. SemVer + CHANGELOG + tags en cada versión estable. +13. Migraciones se aplican vía CLI del VPS (`docker exec -i supabase-db psql`). +14. **Motor de flujo (4C): corre en el cliente** como módulo puro y aislado (entrada→salida + determinista, testeable con Node). Interpreta el esquema declarativo; no embebe lógica + en el componente React. +15. **Validación de integridad server-side en el submit (4G): REQUISITO NO NEGOCIABLE.** + El servidor valida contra el esquema que: las preguntas existen, los valores son opciones + válidas, las instancias corresponden a un generador real, y no se inyectan respuestas a + preguntas que el flujo nunca debió mostrar. La validación de cliente es UX, NO seguridad. + Para datos sensibles de salud, confiar solo en validación de cliente sería una falla de + seguridad. Esto NO se difiere a Fase 2. ## Criterios de éxito (checklist) -- [ ] El formulario funciona end-to-end como wizard, una pregunta por pantalla -- [ ] El mini-wizard de familiares genera instancias correctamente (verificable en DB) +- [ ] El flujo del formulario está definido como esquema declarativo persistido +- [ ] El frontend no contiene reglas de negocio del flujo (verificable por revisión) +- [ ] El wizard funciona end-to-end, una pregunta por pantalla +- [ ] El mini-wizard de familiares genera instancias según el esquema - [ ] Los datos se guardan con `instance_id` correcto por familiar -- [ ] El dashboard sigue funcionando (las preguntas no-instancia usan `instance_id='default'`) -- [ ] Las nuevas RPCs agregan datos de vínculo por instancia -- [ ] El autocomplete de barrios funciona para las ciudades principales -- [ ] La validación visual del wizard coincide con el mockup aprobado -- [ ] `v2.1.0-stable` permanece intacta y restaurable +- [ ] El dashboard sigue funcionando (preguntas no-instancia con `instance_id='default'`) +- [ ] El autocomplete de barrios funciona para ciudades principales +- [ ] La validación visual coincide con el mockup aprobado +- [ ] `v2.1.0-stable` permanece intacta +- [ ] La deuda de Fase 2 está registrada en BACKLOG --- ## Etapas del Sprint 4 -| Etapa | Nombre | Entregable | Dependencia | +| Etapa | Nombre | Entregable | Estado | |---|---|---|---| -| **4.0** | Preservación v2 | Tag v2.1.0, rama stable, dump, CHANGELOG | — | -| **4A** | Modelo de datos | Migración `instance_id` + constraint | 4.0 | -| **4B** | Seed v3 | Instrumento v3 completo en seed.sql + geo JSON | 4A | -| **4C** | Motor del wizard | Grafo de pantallas, estado, navegación (lógica) | 4B | -| **4D** | Componente de pantalla | UI una-pregunta-por-pantalla + secciones | 4C | -| **4E** | Mini-wizard instancias | Vínculos 4.1 + pantallas por familiar | 4D | +| **4.0** | Preservación v2 | Tag, rama stable, dump, CHANGELOG | ✅ Completada | +| **4A** | Persistencia instance_id | Migración + DATA_MODEL | ✅ Completada | +| **4B** | Esquema declarativo + seed v3 | Esquema del formulario como datos + geo JSON | — | +| **4C** | Motor de resolución de flujo | Intérprete del esquema (este formulario) | 4B | +| **4D** | Frontend renderer | Wizard que renderiza el esquema, una preg/pantalla | 4C | +| **4E** | Instancias de vínculo | Mini-wizard de familiares desde el esquema | 4D | | **4F** | Autocomplete barrios | Campo barrio con dataset INE | 4D | -| **4G** | Submit con instance_id | Guardado correcto multi-instancia | 4E | -| **4H** | RPCs de vínculo | Agregados de datos por instancia en dashboard | 4G | -| **4I** | Validación e2e + tag v3.0.0 | QA visual completo + release | todas | - ---- +| **4G** | Submit con instance_id | Guardado multi-instancia | 4E | +| **4H** | RPCs de vínculo | Agregados por instancia en dashboard | 4G | +| **4I** | Validación e2e + tag v3.0.0 | QA visual + release + ADR + BACKLOG Fase 2 | todas | ## Orden de ejecución -4.0 → 4A → 4B → 4C → 4D → (4E ∥ 4F) → 4G → 4H → 4I - -Las etapas 4E y 4F pueden ir en paralelo si hay capacidad; si no, 4E primero -(es la más crítica), 4F después. +4B → 4C → 4D → (4E ∥ 4F) → 4G → 4H → 4I --- -## Notas de versionado por etapa -- Cada etapa cierra con un commit versionado descriptivo. -- Las versiones `3.0.0-alpha.N` se pueden usar para hitos intermedios si se - despliega a un entorno de prueba. -- El tag `v3.0.0` se crea solo en la etapa 4I, cuando todo está validado. \ No newline at end of file +## Definición del esquema declarativo (guía para 4B) + +El esquema describe el formulario como datos. Estructura conceptual: + +``` +formulario: + version, secciones[] + seccion: + numero, nombre, descripcion (el "porqué") + preguntas[] + pregunta: + codigo, tipo, enunciado, hint, opciones[], required, sensitive + flujo: + condiciones_visibilidad[] (cuándo se muestra — evaluado por el motor) + genera_instancias? (si esta pregunta crea instancias, ej. 4.1) + pertenece_a_instancia_de? (de qué generador depende, ej. 4.2/4.3 → 4.1) + validaciones[] +``` + +La forma técnica exacta de persistir esto (columnas en `questions` + +`conditional_rules` JSONB extendido, o tabla de definición de flujo separada) +la decide la Etapa 4B según el esquema actual. Principio: declarativo, versionado, +interpretable por el motor sin conocer el caso de uso. \ No newline at end of file diff --git a/sprints/sprint-4/ETAPA_4C_PROMPT.md b/sprints/sprint-4/ETAPA_4C_PROMPT.md new file mode 100644 index 0000000..d4d448a --- /dev/null +++ b/sprints/sprint-4/ETAPA_4C_PROMPT.md @@ -0,0 +1,124 @@ +# ETAPA 4C — Motor de resolución de flujo (módulo puro, cliente) + +## 1. Objetivo +Construir el motor que interpreta el esquema declarativo del formulario y resuelve +la secuencia de pantallas según el estado de respuestas. Es un módulo puro y aislado +en `lib/`, sin dependencias de React ni del DOM — entrada→salida determinista, +testeable con Node. El componente (Etapa 4D) lo consume; el motor no conoce al componente. + +## 2. Contexto previo +- ADR-005: la lógica de flujo vive en el esquema (datos). El motor la interpreta. +- El esquema v3 está en la DB: questions con `type`, `conditional_rules`, + `instance_generator`, `instance_of`, `hint`, más filas `type='section'`. +- Decisión confirmada: el motor corre en el CLIENTE (interpretación de flujo, no validación + de seguridad — esa va server-side en 4G). +- Decisión confirmada: las secciones (`type='section'`) son PANTALLAS propias en el grafo. +- La lógica de v2 (`lib/conditional.ts`, `survey-form.tsx`) es INCOMPATIBLE y se reemplaza: + - v2 evalúa AND (`rules.every`); v3 necesita OR entre condiciones de ocultamiento. + - v2 solo conoce `eq`/`eq_any` y acción `show`; v3 necesita `contains`, `eq_sole`, `not_in`, acción `hide`. + - v2 infiere secciones por regex de prefijo; v3 las tiene como datos. +- El comportamiento esperado está validado en el mockup `wizard_formulario_mapeo_v3.html` + (función `buildVisible` y navegación) — usar como referencia de comportamiento, NO de arquitectura. + +## 3. DECISIONES YA TOMADAS — no consultar de nuevo +- Motor puro en `lib/` (proponé el nombre: ej. `lib/form-engine.ts`), sin React/DOM. +- Vocabulario de operadores: `eq`, `eq_any`, `not_in` (escalares), `contains`, `eq_sole` (array). +- Acciones: `show`, `hide`. +- Combinación: múltiples reglas en el array de una pregunta se evalúan en OR. + (Una pregunta se oculta si CUALQUIER regla `hide` aplica; se muestra si CUALQUIER regla `show` aplica. + Definí la semántica precisa de show+hide combinados y documentala.) +- Secciones = pantallas propias en el grafo. +- Instancias: 4.1 (`instance_generator=true`) genera N instancias; las preguntas con + `instance_of='4.1'` se repiten una vez por instancia activa, en orden por familiar + (familiar1: 4.2→4.3, familiar2: 4.2→4.3, ...) como en el mockup. +- Estado de respuestas con clave compuesta `question_code:instance_id` (instancias) o + `question_code` con `instance_id='default'` (normales). + +## 4. Tareas + +### 4.1 Tipos del motor (`lib/types.ts` ampliado o nuevo) +Definir/ampliar: +- `ConditionalRule`: operadores y acciones completos del vocabulario v3. +- `FormSchema`: la representación del formulario que el motor consume (secciones, preguntas, + reglas, instanciación) — derivada de las filas de `questions`. +- `WizardState`: respuestas (con instancias), instancia activa, pantalla actual. +- `Screen`: una pantalla resoluble (sección-intro | pregunta | pregunta-de-instancia | fin). + +### 4.2 Evaluador de reglas +Reescribir la evaluación (reemplaza `lib/conditional.ts`): +- Implementar los 5 operadores y 2 acciones. +- `contains` / `eq_sole` operan sobre respuestas array (multiple_choice). +- Semántica OR documentada y correcta para el caso `r_hide_ning_sn`. +- Función pura: `(rule, answers) => boolean` y `(question, answers) => 'show'|'hide'`. + +### 4.3 Resolución del grafo de pantallas +Función pura que dado el esquema + estado devuelve la lista ordenada de pantallas visibles: +- Expande instancias (4.2/4.3 por cada familiar activo declarado en 4.1). +- Aplica visibilidad (oculta secciones/preguntas según reglas). +- Incluye las pantallas de sección. +- Equivale conceptualmente al `buildVisible()` del mockup, pero como módulo puro. + +### 4.4 Navegación +Funciones puras `next(state, schema)` y `back(state, schema)` que devuelven el nuevo +estado (pantalla actual actualizada), resolviendo saltos. Identificar la pantalla actual +por ID estable, no por índice (lección del mockup: el índice se desincroniza cuando +cambian las instancias). + +### 4.5 Carga del esquema +Actualizar la query (`app/page.tsx`) para traer las 3 columnas nuevas +(`hint`, `instance_generator`, `instance_of`). Transformar las filas de `questions` +en el `FormSchema` que consume el motor. + +### 4.6 Tests del motor (Node, sin navegador) +Tests puros que verifiquen: +- Recorrido completo (todos los vínculos) genera las pantallas esperadas. +- "Solo niño sin discapacidad" (eq_sole) → recorrido liviano correcto (5.3, 8.4, 9.1, 9.4). +- "Ninguna de las anteriores" (contains) → salta a sección 10. +- 3.1='No' oculta 3.2-3.5. +- 3 familiares → 6 pantallas de instancia en orden por familiar. +- Navegación next/back coherente cuando cambian las instancias. + +## 5. Entregables (DoD) +- [ ] Motor puro en `lib/` sin dependencias de React/DOM. +- [ ] Los 5 operadores y 2 acciones implementados y testeados. +- [ ] Resolución de grafo con instancias y secciones-pantalla. +- [ ] Navegación por ID estable, no por índice. +- [ ] Query actualizada con las 3 columnas nuevas. +- [ ] Tests Node que cubren los recorridos del punto 4.6, todos en verde. +- [ ] NO se toca el componente todavía (es 4D) — el motor se entrega standalone + tests. + +## 6. Qué reportar +``` +Verificado: +- Motor en [archivo], puro (sin React/DOM) ✓ +- Operadores implementados: eq, eq_any, not_in, contains, eq_sole ✓ +- Acciones: show, hide ✓ — semántica de combinación: [descripción] +- Tests: N/N en verde + - recorrido completo ✓ + - solo niño (eq_sole) → 5.3/8.4/9.1/9.4 ✓ + - ninguna (contains) → salta S10 ✓ + - 3.1=No oculta 3.2-3.5 ✓ + - 3 familiares → 6 pantallas en orden ✓ + - next/back coherente ✓ + +Asumido: +- [supuestos de diseño del motor] +``` + +## 7. Qué NO hacer +- ❌ No poner lógica de flujo en un componente React (el motor es puro, lib/). +- ❌ No tocar el componente de formulario todavía (es 4D). +- ❌ No implementar validación de seguridad acá (es 4G, server-side). +- ❌ No usar índices numéricos para la pantalla actual — usar ID estable. +- ❌ No tocar v2.1.0-stable ni el seed. + +## 8. Versionado +Commit sugerido: +``` +feat(engine): motor de resolución de flujo declarativo [4C] + +Módulo puro que interpreta el esquema v3 y resuelve pantallas. +5 operadores (eq, eq_any, not_in, contains, eq_sole), acciones show/hide. +Instancias de vínculo y secciones-pantalla. Navegación por ID estable. +Tests Node cubren los 3 recorridos. Reemplaza lib/conditional.ts de v2. +``` \ No newline at end of file