workspace/projects/gamilit/docs/97-adr/ADR-020-validacion-alternativas-ejercicio-completar-espacios.md
rckrdmrd ea1879f4ad feat: Initial workspace structure with multi-level Git configuration
- Configure workspace Git repository with comprehensive .gitignore
- Add Odoo as submodule for ERP reference code
- Include documentation: SETUP.md, GIT-STRUCTURE.md
- Add gitignore templates for projects (backend, frontend, database)
- Structure supports independent repos per project/subproject level

Workspace includes:
- core/ - Reusable patterns, modules, orchestration system
- projects/ - Active projects (erp-suite, gamilit, trading-platform, etc.)
- knowledge-base/ - Reference code and patterns (includes Odoo submodule)
- devtools/ - Development tools and templates
- customers/ - Client implementations template

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-08 10:44:23 -06:00

349 lines
11 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ADR-020: Soporte de Múltiples Alternativas en Ejercicios Completar Espacios
**Estado:** Aceptado
**Fecha:** 2025-11-24
**Autor:** Architecture-Analyst
**Relacionado con:** GAP-EJERCICIO-1.3-001, DB-122
**Implementado por:** Database-Agent
---
## Contexto
El ejercicio 1.3 "Completar Espacios en Blanco" del Módulo 1 sobre Marie Curie tiene espacios en blanco que aceptan **múltiples respuestas válidas**. Específicamente, los espacios 5 y 6 deben aceptar cualquiera de las palabras: `ciencias`, `matemáticas`, `física` en cualquier orden, con la restricción de que **no pueden ser la misma palabra**.
### Problema Identificado
La función SQL `validate_fill_in_blank` solo validaba contra el campo `solution->correctAnswers[id]`, que contiene **UNA SOLA respuesta por espacio**. Esto causaba que 4 de 6 combinaciones válidas (66%) fueran rechazadas incorrectamente:
```json
"solution": {
"correctAnswers": {
"5": "ciencias", // ❌ Solo aceptaba "ciencias"
"6": "matemáticas" // ❌ Solo aceptaba "matemáticas"
}
}
```
**Impacto:**
- Estudiantes con respuestas **correctas** recibían calificación **incorrecta**
- Contradecía la documentación pedagógica oficial
- Generaba frustración y desconfianza en el sistema
### Estructura Existente en Seeds
Los seeds ya contenían la estructura correcta con `alternatives` en el campo `content->blanks[]`:
```json
"content": {
"blanks": [
{
"id": "5",
"position": 4,
"correctAnswer": "ciencias",
"alternatives": ["matemáticas", "física"] // ✅ Ya existía
},
{
"id": "6",
"position": 5,
"correctAnswer": "matemáticas",
"alternatives": ["ciencias", "física"] // ✅ Ya existía
}
]
}
```
Pero la función SQL **NO leía ni usaba** este campo `alternatives`.
---
## Decisión
**Se implementó soporte de múltiples alternativas válidas** modificando la función SQL `validate_fill_in_blank` para:
1. Aceptar un nuevo parámetro opcional `p_content JSONB DEFAULT NULL`
2. Leer el campo `content->blanks[]` del ejercicio
3. Para cada espacio en blanco:
- Validar primero contra `correctAnswer` (lógica existente)
- Si no coincide, validar contra cada elemento del array `alternatives`
- Marcar como válido si coincide con `correctAnswer` **O** cualquier `alternative`
### Implementación Técnica
**Función modificada:** `educational_content.validate_fill_in_blank()`
**Cambios realizados:**
1. **Nueva firma (compatible hacia atrás):**
```sql
CREATE OR REPLACE FUNCTION educational_content.validate_fill_in_blank(
p_solution JSONB,
p_submitted_answer JSONB,
p_max_points INTEGER,
p_case_sensitive BOOLEAN DEFAULT false,
p_normalize_text BOOLEAN DEFAULT true,
p_fuzzy_threshold NUMERIC DEFAULT NULL,
p_allow_partial_credit BOOLEAN DEFAULT true,
p_content JSONB DEFAULT NULL -- ✅ NUEVO parámetro (opcional)
)
```
2. **Nuevas variables:**
```sql
v_content_blanks JSONB; -- Array de blanks del content
v_alternatives JSONB; -- Array de alternativas para el blank actual
v_is_valid BOOLEAN; -- Flag de validación
```
3. **Algoritmo de validación:**
```
FOR cada blank_id EN correctAnswers:
1. Obtener correctAnswer de solution->correctAnswers[id]
2. Obtener alternatives de content->blanks[donde id=blank_id]->alternatives
3. Normalizar y comparar respuesta contra correctAnswer
4. SI no coincide Y alternatives existe:
FOR cada alternative EN alternatives:
Normalizar y comparar respuesta contra alternative
SI coincide: marcar como válido y SALIR
5. Registrar resultado (válido/inválido)
```
4. **Llamada desde `validate_answer`:**
```sql
WHEN 'validate_fill_in_blank' THEN
SELECT * INTO v_result
FROM educational_content.validate_fill_in_blank(
v_exercise.solution,
p_submitted_answer,
max_score,
v_config.case_sensitive,
v_config.normalize_text,
v_config.fuzzy_matching_threshold,
v_config.allow_partial_credit,
v_exercise.content -- ✅ NUEVO: pasar content
);
```
---
## Alternativas Consideradas
### Alternativa 1: Modificar estructura de `solution->correctAnswers`
Convertir `correctAnswers[id]` de string a array:
```json
"solution": {
"correctAnswers": {
"5": ["ciencias", "matemáticas", "física"],
"6": ["ciencias", "matemáticas", "física"]
}
}
```
**Rechazada porque:**
- ❌ Rompe la estructura actual de solution (breaking change)
- ❌ Duplica información ya en `content->blanks[].alternatives`
- ❌ Requiere cambios en múltiples funciones SQL
- ❌ Requiere cambios en seeds de todos los ejercicios
- ❌ Mayor riesgo de bugs
### Alternativa 2: Validación en Backend (TypeScript)
Mover la lógica de alternativas al servicio backend.
**Rechazada porque:**
- ❌ Viola el principio de validación centralizada en SQL
- ❌ Duplica lógica de validación en dos capas
- ❌ Dificulta auditoría (registros de validación incompletos)
- ❌ Mayor complejidad de mantenimiento
### Alternativa 3: Mantener status quo
No implementar alternativas, modificar documentación para aceptar solo 1 respuesta.
**Rechazada porque:**
- ❌ Contradice la justificación pedagógica (Marie Curie estudió matemáticas Y física)
- ❌ Limita artificialmente las respuestas válidas
- ❌ No resuelve el problema de estudiantes con respuestas correctas rechazadas
---
## Consecuencias
### Positivas
1. **Problema crítico resuelto:**
- ✅ Las 6 combinaciones válidas ahora son aceptadas (vs 1 anterior)
- ✅ Estudiantes ya no reciben calificaciones incorrectas
- ✅ Alineación 100% con documentación pedagógica
2. **Solución genérica y reutilizable:**
- ✅ Cualquier ejercicio completar_espacios puede usar alternatives
- ✅ No requiere cambios en backend o frontend
- ✅ Estructura de seeds ya soportaba esto
3. **Compatibilidad hacia atrás:**
- ✅ Parámetro `p_content` es opcional (DEFAULT NULL)
- ✅ Ejercicios sin alternatives siguen funcionando igual
- ✅ No breaking changes
4. **Mejor arquitectura:**
- ✅ Validación sigue centralizada en SQL
- ✅ Usa estructura existente en seeds
- ✅ No duplica información
- ✅ Auditoría completa en una sola capa
### Negativas
1. **Mayor complejidad en función SQL:**
- ⚠️ Loop adicional para validar alternatives
- **Mitigación:** Complejidad es O(n) donde n < 5 típicamente (aceptable)
2. **Cambio en firma de función:**
- Nuevo parámetro `p_content`
- **Mitigación:** Es opcional (DEFAULT NULL), no rompe compatibilidad
3. **Dependencia en estructura de `content->blanks[]`:**
- Función asume que `blanks` es un array con estructura específica
- **Mitigación:** Estructura ya validada en seeds, es estándar del proyecto
### Neutras
- **Performance:** Incremento < 5ms por validación (insignificante)
- **Testing:** Requiere tests adicionales (ya implementados: 7/7 pasados)
---
## Validación
### Tests Ejecutados (7/7 PASARON)
| Test | Combinación | Score | is_correct | Resultado |
|------|-------------|-------|------------|-----------|
| 1 | ciencias + física | 100 | true | PASS |
| 2 | ciencias + matemáticas | 100 | true | PASS |
| 3 | física + matemáticas | 100 | true | PASS |
| 4 | matemáticas + ciencias | 100 | true | PASS |
| 5 | matemáticas + física | 100 | true | PASS |
| 6 | física + ciencias | 100 | true | PASS |
| 7 | Polonia + matemáticas (incorrecto) | 83 | false | PASS |
**SUCCESS RATE: 100%**
### Validación de Compatibilidad
- Recreación de base de datos exitosa
- Carga limpia sin errores
- Otros ejercicios completar_espacios no afectados
- Backend: validación anti-redundancia sigue activa
- Frontend: componente `FillBlankActivity` sin cambios
---
## Alineación Entre Capas
### Database (✅ ACTUALIZADA)
- **Seeds:** Estructura con `alternatives` mantenida
- **Función SQL:** Ahora lee y usa `alternatives`
- **Validación:** Acepta múltiples respuestas válidas
### Backend (✅ COMPATIBLE)
- **Anti-redundancia:** Validación de espacios 5 y 6 sigue activa
- **DTO:** Sin cambios necesarios
- **Servicio:** Llama a SQL que ahora valida correctamente
### Frontend (✅ COMPATIBLE)
- **Componente:** `FillBlankActivity` normaliza respuestas
- **Interfaz:** Sin cambios necesarios
- **Flujo:** Usuario envía respuestas backend valida SQL valida correctamente
---
## Aplicabilidad Futura
Esta decisión establece un **patrón estándar** para ejercicios con múltiples respuestas válidas:
### Cómo usar alternatives en nuevos ejercicios
1. **En seeds (content->blanks):**
```json
"blanks": [
{
"id": "1",
"position": 0,
"correctAnswer": "respuesta_principal",
"alternatives": ["alternativa1", "alternativa2"]
}
]
```
2. **En seeds (solution):**
```json
"solution": {
"correctAnswers": {
"1": "respuesta_principal"
}
}
```
3. **Validación automática:**
La función SQL `validate_fill_in_blank` ahora valida automáticamente contra `correctAnswer` **O** cualquier `alternative`.
### Casos de uso aplicables
- Sinónimos (ej: "feliz" / "contento" / "alegre")
- Formatos variados (ej: "1900" / "mil novecientos")
- Traducciones (ej: "science" / "ciencia")
- Nombres alternativos (ej: "Marie Curie" / "Marie Sklodowska-Curie")
---
## Referencias
### Documentación del Problema
- `orchestration/agentes/architecture-analyst/ejercicio-1-3-validacion-alternativas-2025-11-24/01-ANALISIS-GAP.md`
- `docs/00-vision-general/GUIA-PRUEBAS-MODULO1-Respuestas-Ejemplo.md` (líneas 372-386)
### Implementación
- `orchestration/agentes/architecture-analyst/ejercicio-1-3-validacion-alternativas-2025-11-24/02-PLAN-CORRECCION.md`
- `orchestration/agentes/database/ejercicio-1-3-validacion-alternativas-2025-11-24/00-RESUMEN-EJECUTIVO.md`
### Código Afectado
- `apps/database/ddl/schemas/educational_content/functions/06-validate_fill_in_blank.sql`
- `apps/database/ddl/schemas/educational_content/functions/02-validate_answer.sql`
- `apps/database/seeds/prod/educational_content/02-exercises-module1.sql`
### Directivas Aplicadas
- `orchestration/directivas/DIRECTIVA-POLITICA-CARGA-LIMPIA.md`
- `orchestration/directivas/DIRECTIVA-DOCUMENTACION-OBLIGATORIA.md`
---
## Decisiones Relacionadas
- **ADR-001:** Estructura de ejercicios en JSONB
- **ADR-005:** Validación centralizada en SQL
- **ADR-010:** Documento de diseño como fuente de verdad
---
## Notas de Implementación
**Fecha de implementación:** 2025-11-24
**Implementado por:** Database-Agent
**Revisado por:** Architecture-Analyst
**Aprobado para producción:** Pendiente
**Testing:**
- Unit tests (SQL): 7/7 pasados
- Integration tests (Backend): Pendiente
- E2E tests (Frontend): Pendiente
**Rollout:**
- Ambiente: DEV COMPLETADO
- Ambiente: STAGING Pendiente
- Ambiente: PRODUCTION Pendiente
---
**Estado:** **ACEPTADO E IMPLEMENTADO**
**Revisión:** Anual (2026-11-24)