# 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. ```