Files
motor-de-encuestas/sprints/sprint-4/ETAPA_4G_PROMPT.md
markosbenitez eaf71567a7 prompt 4G
2026-06-24 21:29:01 -03:00

121 lines
6.8 KiB
Markdown

# ETAPA 4G — Submit con instance_id + validación de integridad server-side
## 1. Objetivo
Escribir el submit de v3: persiste las respuestas (incluyendo instancias) y VALIDA la
integridad en el servidor contra el esquema antes de insertar. La validación server-side
es REQUISITO NO NEGOCIABLE (BRIEF decisión 15). El cliente no es confiable; datos de salud.
## 2. Contexto previo
- El submit de v2 (`app/actions/submit-survey.ts`) NO está conectado y está desactualizado:
- `WITH CHECK (true)` en RLS de answers → acepta cualquier insert (sin validación real).
- payload plano `Record<code,value>` — sin noción de `instance_id`.
- códigos qi_* apuntan a A1/A4/A5 (v2), no existen en v3 (son 2.1/2.4/2.5).
- 3 INSERTs sueltos sin transacción → si answers falla, responses/consent quedan huérfanos.
- El motor `lib/form-engine.ts` es PURO → se puede correr SERVER-SIDE para validar.
NO reescribir la lógica de flujo: reusar el motor.
- `answers.instance_id` existe (4A), constraint UNIQUE(response_id, question_id, instance_id).
- El estado del wizard ya tiene claves `code:instanceId` (4C/4D).
- Se usa anon key respetando RLS (regla 9 de CLAUDE.md) — NUNCA service_role.
## 3. DECISIONES YA TOMADAS — no consultar de nuevo
- Validación de integridad SERVER-SIDE, no negociable.
- Reusar `lib/form-engine.ts` en el servidor para validar coherencia de flujo — no duplicar lógica.
- El submit rechaza el envío ENTERO si algo no valida — no inserta datos parciales ni inyectados.
- El payload del cliente pasa a ser consciente de instancia: `{code, instanceId, value}[]`
(o equivalente que preserve la clave compuesta).
- Transaccionalidad: las inserciones (consent_records → responses → answers) deben ser atómicas.
Evaluar función RPC transaccional (SECURITY DEFINER con validaciones) vs. inserts en el
Server Action. Proponé el enfoque más robusto dado que anon no puede hacer múltiples
inserts atómicos fácilmente — una RPC transaccional es probablemente lo correcto.
- Códigos qi_* actualizados a v3 (2.1 zona, 2.4 edad, 2.5 sector — verificar contra el seed).
## 4. Validaciones server-side mandatorias
Por cada submit recibido, el servidor valida CONTRA EL ESQUEMA de la DB (no contra el cliente):
1. **Survey válido y activo:** el `survey_id` existe y está en estado que admite respuestas.
2. **Preguntas legítimas:** cada `code` recibido existe y pertenece a ESE survey.
3. **Valores válidos:** cada valor es una opción válida de su pregunta (para single/multiple_choice),
respeta `max_select`, tipo correcto.
4. **Visibilidad coherente:** usando el motor (`resolveScreens`/`resolveVisibility`) con las
respuestas recibidas, cada respuesta enviada corresponde a una pregunta que DEBÍA estar
visible. Rechazar respuestas a preguntas que el flujo nunca debió mostrar (ej. responder
3.2 cuando 3.1='No' las oculta).
5. **Instancias legítimas:** cada `instance_id` (familiar_N) corresponde a una instancia
realmente generada por su `instance_generator` (4.1) según la respuesta real recibida para 4.1.
Rechazar instancias inventadas.
6. **Consentimiento requerido presente:** el consentimiento operativo (requerido) está.
7. **Requeridas presentes:** las preguntas required visibles tienen respuesta.
Si CUALQUIERA falla: rechazar el submit completo, no insertar nada, devolver error claro.
## 5. Tareas
### 5.1 Definir el contrato del payload v3
Shape consciente de instancia. El wizard ya tiene `state.answers` con claves `code:instanceId`.
Definir el payload que el cliente envía y el validador consume.
### 5.2 Validador server-side (reusando el motor)
Módulo/función que recibe el payload + carga el esquema desde la DB + corre las 7 validaciones
del punto 4, reusando `lib/form-engine.ts`. Puro y testeable donde se pueda.
### 5.3 Persistencia transaccional
Implementar la inserción atómica (consent_records → responses → answers con instance_id).
Proponer e implementar RPC transaccional o mecanismo equivalente. Si una RPC SECURITY DEFINER
es el camino, documentar por qué y qué valida internamente.
### 5.4 Conectar el wizard al submit
El botón "Enviar (próximamente)" pasa a enviar de verdad. Manejar estados éxito/error
(los stage que 4D dejó sin implementar). Mensajes de error accionables, nunca trace crudo.
### 5.5 Tests
- Validación: submit con respuesta a pregunta oculta → rechazado.
- Validación: valor fuera de opciones → rechazado.
- Validación: instancia inventada → rechazada.
- Validación: sin consentimiento requerido → rechazado.
- Happy path: submit completo válido (con instancias) → persiste correctamente con instance_id.
- Atomicidad: fallo a mitad → no quedan datos parciales.
## 6. Entregables (DoD)
- [ ] Validación server-side completa (7 validaciones) reusando el motor.
- [ ] Submit rechaza envío entero si algo no valida; sin datos parciales.
- [ ] Persistencia transaccional (sin huérfanos ante fallo).
- [ ] instance_id persistido correctamente por instancia.
- [ ] Códigos qi_* actualizados a v3.
- [ ] Wizard conectado al submit, estados éxito/error funcionando.
- [ ] Tests de las validaciones + happy path + atomicidad.
- [ ] anon key, nunca service_role (salvo dentro de RPC SECURITY DEFINER justificada).
- [ ] **Validación del director** antes de aplicar a producción.
## 7. Qué reportar
```
Verificado:
- Validación server-side: 7/7 implementadas, reusando lib/form-engine.ts ✓
- Casos rechazados probados: pregunta oculta, valor inválido, instancia falsa, sin consentimiento
- Persistencia transaccional vía [RPC / mecanismo] ✓ — atomicidad probada
- instance_id persistido: [ejemplo real de filas answers con familiar_1/familiar_2]
- Códigos qi_* actualizados a v3 ✓
- Wizard conectado, estados éxito/error ✓
- Tests: N/N
Asumido / decisiones:
- [enfoque transaccional y por qué]
Hallazgos:
- [lo que aparezca]
```
Reporte con datos concretos (C.4): mostrá filas reales de answers con instance_id tras un
submit de prueba con 2 familiares.
## 8. Qué NO hacer
- ❌ No confiar en validación de cliente para integridad (es UX, no seguridad).
- ❌ No duplicar la lógica de flujo — reusar el motor server-side.
- ❌ No insertar datos parciales si la validación falla.
- ❌ No usar service_role fuera de una RPC SECURITY DEFINER justificada y documentada.
- ❌ No aplicar a producción sin validación del director.
- ❌ No tocar v2.1.0-stable, el seed, ni el motor (salvo para CONSUMIRLO en validación).
## 9. Versionado
```
feat(submit): submit v3 con validación de integridad server-side [4G]
Validación server-side (no negociable) reusando el motor: rechaza respuestas
a preguntas ocultas, valores inválidos, instancias falsas, sin consentimiento.
Persistencia transaccional con instance_id por instancia. Conecta el wizard.
Cierra la falla de WITH CHECK(true): el cliente ya no es fuente de verdad.
```