Cambios incluidos: - INDICE-DIRECTIVAS-WORKSPACE.yml actualizado - Perfiles de agentes: PERFIL-ML.md, PERFIL-SECURITY.md - Directivas SIMCO actualizadas: - SIMCO-ASIGNACION-PERFILES.md - SIMCO-CCA-SUBAGENTE.md - SIMCO-CONTEXT-ENGINEERING.md - SIMCO-CONTEXT-RESOLUTION.md - SIMCO-DELEGACION-PARALELA.md - Inventarios actualizados: DEVENV-MASTER, DEVENV-PORTS - Documentos de analisis agregados: - Analisis y planes de fix student portal - Analisis scripts BD - Analisis achievements, duplicados, gamification - Auditoria documentacion gamilit - Backlog discrepancias NEXUS - Planes maestros de resolucion - Reportes de ejecucion agregados - Knowledge base gamilit README actualizado - Referencia submodulo gamilit actualizada (commit beb94f7) Validaciones: - Plan validado contra directivas SIMCO-GIT - Dependencias verificadas - Build gamilit: EXITOSO
8.5 KiB
VALIDACIÓN DE PLAN: CORRECCIONES ACHIEVEMENTS PAGE
Fecha: 2026-01-10 Proyecto: Gamilit Componente: /achievements (Student Portal) Estado: VALIDADO ✅
1. RESUMEN DE VALIDACIÓN
Se han verificado los archivos fuente contra el plan propuesto. Se confirman los siguientes hallazgos:
✅ Problemas Confirmados
| ID | Problema | Estado | Archivo | Línea(s) |
|---|---|---|---|---|
| P1 | Función SQL usa columnas inexistentes | ✅ CONFIRMADO | claim_achievement_reward.sql |
35, 54, 70 |
| P2 | Store usa || en lugar de ?? |
✅ CONFIRMADO | achievementsStore.ts |
172-173 |
| P3 | completion_percentage no se parsea |
⚠️ PARCIAL* | achievementsStore.ts |
N/A |
| P8 | Category mapping incompleto | ✅ CONFIRMADO | achievementsAPI.ts |
346-360 |
*P3: La API (achievementsAPI.ts) SÍ hace el parseFloat correctamente (línea 162-163), pero el Store hace su propio mapeo y NO parsea.
2. VALIDACIÓN DETALLADA: BASE DE DATOS
2.1 Función claim_achievement_reward.sql
Columnas verificadas contra DDL:
| Columna en función | ¿Existe en DDL? | Columna correcta |
|---|---|---|
reward_claimed_at |
❌ NO | rewards_claimed (BOOLEAN) |
xp_reward |
❌ NO | rewards->>'xp' o points_value |
ml_coins_reward |
✅ SÍ | ml_coins_reward (INTEGER) |
Errores línea por línea:
-- LÍNEA 35 (ERROR)
v_already_claimed := v_user_achievement.reward_claimed_at IS NOT NULL;
-- CORRECCIÓN:
v_already_claimed := v_user_achievement.rewards_claimed = TRUE;
-- LÍNEA 54 (ERROR)
SET reward_claimed_at = NOW()
-- CORRECCIÓN:
SET rewards_claimed = TRUE
-- LÍNEA 70 (ERROR)
total_xp = total_xp + v_achievement.xp_reward,
-- CORRECCIÓN:
total_xp = total_xp + COALESCE((v_achievement.rewards->>'xp')::INTEGER, v_achievement.points_value, 0),
2.2 Verificación de DDL
Tabla user_achievements (confirmado):
-- Columnas existentes (líneas 33-52):
rewards_claimed boolean DEFAULT false -- Línea 44 ✅
completed_at timestamp with time zone -- Línea 41 ✅
-- NO existe: reward_claimed_at
Tabla achievements (confirmado):
-- Columnas existentes (líneas 41-66):
rewards jsonb DEFAULT '{"xp": 100, "badge": null, "ml_coins": 50}' -- Línea 51 ✅
points_value integer DEFAULT 0 -- Línea 56 ✅
ml_coins_reward integer DEFAULT 0 -- Línea 64 ✅
-- NO existe: xp_reward
3. VALIDACIÓN DETALLADA: FRONTEND
3.1 achievementsStore.ts
Problema confirmado en líneas 172-173:
// ACTUAL (línea 172-173):
mlCoinsReward: ach.rewards?.ml_coins || ach.ml_coins_reward || 0,
xpReward: ach.rewards?.xp || 0,
// PROBLEMA: || convierte 0 en falsy y usa fallback incorrectamente
// Si ml_coins = 0, usará ml_coins_reward en lugar de respetar el 0
Corrección requerida:
mlCoinsReward: ach.rewards?.ml_coins ?? ach.ml_coins_reward ?? 0,
xpReward: ach.rewards?.xp ?? ach.points_value ?? 0,
3.2 achievementsAPI.ts
Ya corregido correctamente (líneas 383-385):
// CORRECTO - Usa ??
mlCoinsReward: backendAchievement.rewards?.ml_coins ?? backendAchievement.ml_coins_reward ?? 0,
xpReward: backendAchievement.rewards?.xp ?? backendAchievement.points_value ?? 0,
Category mapping incompleto (líneas 346-360):
// ACTUAL:
const categoryMap: Record<string, 'progress' | 'mastery' | 'social' | 'hidden'> = {
educational: 'progress',
progress: 'progress',
mastery: 'mastery',
skill: 'mastery',
social: 'social',
hidden: 'hidden',
special: 'hidden',
collection: 'mastery',
missions: 'progress',
};
// FALTAN (del ENUM achievement_category):
// - streak → debería mapear a 'progress' o nuevo tipo
// - exploration → debería mapear a 'progress' o nuevo tipo
// - completion → debería mapear a 'progress' o nuevo tipo
Tipo de retorno limitado:
El tipo de retorno es 'progress' | 'mastery' | 'social' | 'hidden' pero el ENUM tiene 9 valores:
progress,streak,completion,social,special,mastery,exploration,collection,hidden
4. DEPENDENCIAS VALIDADAS
4.1 Archivos que serán modificados
| Archivo | Dependencias | Riesgo |
|---|---|---|
claim_achievement_reward.sql |
Ninguna directa, se llama desde backend | 🟡 Medio - afecta reclamar recompensas |
achievementsStore.ts |
achievementsAPI.ts, componentes React |
🟡 Medio - afecta visualización |
achievementsAPI.ts |
Solo mapeo interno | 🟢 Bajo - cambio aislado |
4.2 Archivos que NO necesitan cambios
| Archivo | Razón |
|---|---|
AchievementsPage.tsx |
Usa gamificationApi, no achievementsStore directamente |
achievements.service.ts |
Backend usa TypeORM, no función SQL |
| DDL de tablas | Estructura correcta, no requiere cambios |
5. PLAN DE CORRECCIÓN VALIDADO
FASE A: Base de Datos (CRÍTICO)
Archivo: claim_achievement_reward.sql
Cambios específicos:
-- Línea 35: Cambiar verificación de reclamado
-- DE: v_already_claimed := v_user_achievement.reward_claimed_at IS NOT NULL;
-- A: v_already_claimed := v_user_achievement.rewards_claimed = TRUE;
-- Línea 54: Cambiar actualización de estado
-- DE: SET reward_claimed_at = NOW()
-- A: SET rewards_claimed = TRUE
-- Línea 70: Cambiar obtención de XP
-- DE: total_xp = total_xp + v_achievement.xp_reward,
-- A: total_xp = total_xp + COALESCE((v_achievement.rewards->>'xp')::INTEGER, v_achievement.points_value, 0),
FASE B: Frontend Store (CRÍTICO)
Archivo: achievementsStore.ts
Cambios específicos:
// Líneas 172-173: Usar nullish coalescing
// DE:
mlCoinsReward: ach.rewards?.ml_coins || ach.ml_coins_reward || 0,
xpReward: ach.rewards?.xp || 0,
// A:
mlCoinsReward: ach.rewards?.ml_coins ?? ach.ml_coins_reward ?? 0,
xpReward: ach.rewards?.xp ?? ach.points_value ?? 0,
FASE C: Frontend API (IMPORTANTE)
Archivo: achievementsAPI.ts
Cambios específicos:
// Líneas 346-360: Expandir mapeo de categorías
const categoryMap: Record<string, Achievement['category']> = {
educational: 'progress',
progress: 'progress',
streak: 'progress', // AGREGAR
completion: 'progress', // AGREGAR
exploration: 'progress', // AGREGAR
mastery: 'mastery',
skill: 'mastery',
collection: 'mastery',
social: 'social',
hidden: 'hidden',
special: 'hidden',
missions: 'progress',
};
6. VERIFICACIÓN DE SEEDS
Seeds de Achievements (04-achievements.sql)
Estructura de conditions verificada:
-- Ejemplo correcto de seed:
conditions = '{"type": "exercise_completion", "requirements": {"exercises_completed": 1}}'::jsonb
-- Estructura esperada por backend: ✅ CORRECTA
Estructura de rewards verificada:
-- Ejemplo correcto de seed:
rewards = '{"xp": 50, "ml_coins": 10, "badge": null}'::jsonb
-- Estructura esperada: ✅ CORRECTA
Seeds de User Achievements (08-user_achievements.sql)
Campos verificados:
user_id→ FK válido ✅achievement_id→ FK válido ✅rewards_claimed→ BOOLEAN ✅completed_at→ TIMESTAMPTZ ✅
7. RIESGOS IDENTIFICADOS
7.1 Riesgo: Función SQL en producción
Escenario: Si la función claim_achievement_reward() se está usando en producción, fallará con:
ERROR: column "reward_claimed_at" does not exist
Mitigación:
- Verificar si hay llamadas activas a la función
- Aplicar corrección en ventana de bajo tráfico
7.2 Riesgo: Datos legacy con completion_percentage string
Escenario: Registros existentes pueden tener completion_percentage como string.
Mitigación:
- El Store NO parsea, pero la API SÍ lo hace
- Si el Store se usa directamente sin pasar por API, podría fallar
7.3 Riesgo: Categorías nuevas en backend
Escenario: Si el backend agrega nuevas categorías al ENUM, el frontend las mostrará como 'progress'.
Mitigación:
- Actualizar mapeo cuando se agreguen nuevas categorías
- El default a 'progress' es seguro (fail-safe)
8. CONCLUSIÓN
✅ PLAN VALIDADO
El plan de corrección es viable y los cambios propuestos son correctos.
Próximos pasos:
- ✅ Fase A: Corregir función SQL
- ✅ Fase B: Corregir Store con nullish coalescing
- ✅ Fase C: Expandir mapeo de categorías
- ⏳ Fase D: Validar seeds (ya verificado - correcto)
- ⏳ Fase E: Testing end-to-end
Validado por: Claude (Arquitecto Técnico) Fecha: 2026-01-10 Siguiente Fase: Refinamiento del plan → Ejecución