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
20 KiB
FASE 1: ANÁLISIS INICIAL DE DUPLICADOS - ACHIEVEMENTS SYSTEM
Fecha: 2026-01-10 Proyecto: Gamilit Componente: Sistema de Achievements (Full Stack) Estado: EN ANÁLISIS
1. RESUMEN EJECUTIVO
Se identificaron múltiples duplicaciones y conflictos críticos en el sistema de achievements a través de las tres capas (Database, Backend, Frontend):
| Capa | Duplicados | Severidad |
|---|---|---|
| Database (SQL) | 4 funciones con overlap | 🔴 CRÍTICO |
| Backend (NestJS) | 3 servicios con lógica inconsistente | 🔴 CRÍTICO |
| Frontend (React) | 6+ archivos duplicados | 🟡 IMPORTANTE |
2. HALLAZGOS POR CAPA
2.1 BASE DE DATOS - Funciones SQL Duplicadas
CRÍTICO: Flujo de Recompensas Inconsistente
| Función | Otorga XP | Otorga Coins | Registra Transacción | Usa Multiplicador |
|---|---|---|---|---|
claim_achievement_reward |
✅ | ✅ | ✅ | ❌ |
check_and_award_achievements |
✅ | ✅ | ✅ | ❌ |
award_ml_coins |
❌ | ✅ | ✅ | ✅ (rank) |
process_exercise_completion |
✅ | ✅ | ❌ | ❌ |
promote_to_next_rank |
❌ | ✅ | ✅ | ❌ |
consume_comodin |
✅ | ❌ | ❌ | ❌ |
Problema Principal: claim_achievement_reward y check_and_award_achievements duplican funcionalidad:
check_and_award_achievements: Otorga recompensas AL DESBLOQUEARclaim_achievement_reward: Otorga recompensas AL RECLAMAR
Riesgo: Si ambas se ejecutan, el usuario recibe recompensas DUPLICADAS.
Funciones SQL Relacionadas
/apps/database/ddl/schemas/gamification_system/functions/
├── claim_achievement_reward.sql # MODIFICADA - Reclamar recompensas
├── check_and_award_achievements.sql # DUPLICACIÓN - Otorga al desbloquear
├── award_ml_coins.sql # HELPER - Con multiplicador de rank
├── process_exercise_completion.sql # PARCIAL - XP/coins sin transacción
├── promote_to_next_rank.sql # RANK - Bonus por promoción
├── update_user_rank.sql # RANK - Similar a promote
├── check_rank_promotion.sql # RANK - Orquestación
├── consume_comodin.sql # ITEMS - XP sin transacción
└── apply_xp_boost.sql # HELPER - Solo lectura
2.2 BACKEND - Servicios con Lógica Inconsistente
CRÍTICO: claimRewards() NO Distribuye Recompensas
Archivo: achievements.service.ts
// PROBLEMA: Solo marca como reclamado, NO distribuye XP/Coins
async claimRewards(userId: string, achievementId: string): Promise<UserAchievement> {
// ... validaciones ...
userAchievement.rewards_claimed = true;
return this.userAchievementRepo.save(userAchievement);
// ❌ FALTA: Llamar a UserStatsService.addXp()
// ❌ FALTA: Llamar a MLCoinsService.addCoins()
}
Contraste con otros servicios:
| Servicio | Método | Distribuye Rewards |
|---|---|---|
achievements.service.ts |
claimRewards() |
❌ NO |
exercise-rewards.service.ts |
claimRewards() |
✅ SÍ (XP + Coins) |
mission-claim.service.ts |
claimMission() |
✅ SÍ (XP + Coins) |
CRÍTICO: Backend NO Llama a claim_achievement_reward SQL
Hallazgo: Ningún archivo del backend invoca la función SQL claim_achievement_reward.
- La función SQL está corregida pero NO se usa
- El backend usa TypeORM directamente para actualizar
rewards_claimed - Las recompensas (XP/Coins) NO se distribuyen en absoluto
Schema Mismatch en Admin
Archivo: admin-progress.service.ts
| Campo SQL Query | Campo en Entity | Problema |
|---|---|---|
a.tier |
a.rarity / a.difficulty_level |
❌ No existe |
ua.progress_current |
ua.progress |
❌ Nombre diferente |
ua.progress_required |
ua.max_progress |
❌ Nombre diferente |
ua.unlocked_at |
ua.completed_at |
❌ Nombre diferente |
2.3 FRONTEND - Archivos Duplicados
APIs Duplicadas (3 archivos)
| Archivo | Ubicación | Métodos Duplicados |
|---|---|---|
achievementsAPI.ts |
/features/gamification/social/api/ |
claimAchievementRewards() |
gamification.api.ts |
/lib/api/ |
claimAchievement() |
achievementsApi.ts |
/services/api/admin/ |
Admin-only (OK) |
Problema: claimAchievementRewards() y claimAchievement() son implementaciones diferentes del mismo endpoint.
Hooks Duplicados (4 archivos)
| Hook | Ubicación | Líneas | Propósito |
|---|---|---|---|
useAchievements.ts |
/hooks/ |
~450 | Definiciones hardcodeadas + polling |
useAchievements.ts |
/features/gamification/social/hooks/ |
~80 | Wrapper del store |
useAchievementsEnhanced.ts |
/apps/student/hooks/ |
~300 | Filtrado avanzado |
useAchievementsStats.ts |
/apps/teacher/hooks/ |
~50 | Analytics (OK - diferente) |
Problema Principal: /hooks/useAchievements.ts tiene 450+ líneas de definiciones de achievements hardcodeadas que deberían venir del backend.
Transformers Inconsistentes
| Archivo | Approach |
|---|---|
achievementsAPI.ts |
Mappers INLINE (mapToFrontendAchievement) |
achievementTransformer.ts |
Transformer EXTERNO (transformAchievements) |
gamification.api.ts |
Usa transformer externo |
3. MATRIZ DE DUPLICACIÓN
┌─────────────────────────────────────────────────────────────────────────────┐
│ FLUJO DE CLAIM REWARDS │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Frontend Backend Database │
│ ───────── ─────── ──────── │
│ │
│ achievementsAPI.ts ──► achievements.service.ts ──► [NO LLAMA SQL] │
│ claimAchievementRewards() claimRewards() claim_achievement_reward│
│ │ │ │
│ │ │ ❌ Solo marca flag │
│ │ │ ❌ NO distribuye rewards │
│ │ │ │
│ gamification.api.ts ───► [MISMO ENDPOINT] │
│ claimAchievement() │
│ │
│ ════════════════════════════════════════════════════════════════════════ │
│ PROBLEMA: Las recompensas NUNCA se distribuyen al reclamar achievements │
│ ════════════════════════════════════════════════════════════════════════ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
4. ARCHIVOS IDENTIFICADOS PARA ANÁLISIS DETALLADO
4.1 Database (SQL) - 9 archivos
/apps/database/ddl/schemas/gamification_system/functions/
├── claim_achievement_reward.sql # MODIFICADO
├── check_and_award_achievements.sql # DUPLICACIÓN POTENCIAL
├── award_ml_coins.sql # HELPER - Debería usarse
├── process_exercise_completion.sql # INCONSISTENTE
├── promote_to_next_rank.sql # OVERLAP CON update_user_rank
├── update_user_rank.sql # OVERLAP CON promote
├── check_rank_promotion.sql # ORQUESTACIÓN
├── consume_comodin.sql # INCONSISTENTE
└── update_leaderboard_streaks.sql # MENOR
4.2 Backend (NestJS) - 6 archivos
/apps/backend/src/modules/
├── gamification/
│ ├── services/
│ │ ├── achievements.service.ts # CRÍTICO - NO distribuye rewards
│ │ └── missions/mission-claim.service.ts # REFERENCIA - SÍ distribuye
│ ├── controllers/achievements.controller.ts
│ └── entities/
│ ├── achievement.entity.ts
│ └── user-achievement.entity.ts
├── progress/services/
│ └── grading/exercise-rewards.service.ts # REFERENCIA - SÍ distribuye
└── admin/services/
└── admin-progress.service.ts # SCHEMA MISMATCH
4.3 Frontend (React) - 8 archivos
/apps/frontend/src/
├── features/gamification/
│ ├── social/
│ │ ├── api/achievementsAPI.ts # MODIFICADO
│ │ ├── store/achievementsStore.ts # MODIFICADO
│ │ ├── hooks/useAchievements.ts # WRAPPER
│ │ └── types/achievementsTypes.ts
│ └── achievements/utils/
│ └── achievementTransformer.ts # NO USADO CONSISTENTEMENTE
├── hooks/useAchievements.ts # DUPLICADO - 450 líneas hardcoded
├── apps/student/hooks/useAchievementsEnhanced.ts
└── lib/api/gamification.api.ts # DUPLICADO
5. PROBLEMAS CRÍTICOS IDENTIFICADOS
P-DUP-001: Recompensas NO Distribuidas al Reclamar
- Severidad: 🔴 CRÍTICO
- Impacto: Los usuarios reclaman achievements pero NO reciben XP/ML Coins
- Causa: Backend no llama función SQL ni servicios de distribución
- Archivos:
achievements.service.ts,claim_achievement_reward.sql
P-DUP-002: Doble Otorgamiento de Recompensas
- Severidad: 🔴 CRÍTICO
- Impacto: Si se usa
check_and_award_achievements+claim_achievement_reward, rewards duplicados - Causa: Dos funciones con mismo propósito
- Archivos:
check_and_award_achievements.sql,claim_achievement_reward.sql
P-DUP-003: APIs Frontend Duplicadas
- Severidad: 🟡 IMPORTANTE
- Impacto: Inconsistencia en manejo de errores y respuestas
- Causa: Dos archivos con mismo endpoint
- Archivos:
achievementsAPI.ts,gamification.api.ts
P-DUP-004: Hook con Definiciones Hardcodeadas
- Severidad: 🟡 IMPORTANTE
- Impacto: Achievements pueden no coincidir con backend
- Causa: 450 líneas de definiciones en frontend
- Archivos:
/hooks/useAchievements.ts
P-DUP-005: Schema Mismatch Admin
- Severidad: 🟡 IMPORTANTE
- Impacto: Dashboard admin muestra datos incorrectos o NULL
- Causa: Campos SQL no coinciden con entities
- Archivos:
admin-progress.service.ts
P-DUP-006: Transformers Inconsistentes
- Severidad: 🟢 MENOR
- Impacto: Código duplicado de transformación
- Causa: Inline mappers vs external transformer
- Archivos:
achievementsAPI.ts,achievementTransformer.ts
6. SIGUIENTE FASE
FASE 2: Análisis detallado de cada archivo identificado para:
- Confirmar duplicaciones con comparación línea a línea
- Identificar dependencias entre archivos
- Determinar cuál versión mantener/consolidar
- Mapear impacto de cambios
7. FASE 2: ANÁLISIS DETALLADO
7.1 Comparación SQL: claim_achievement_reward vs check_and_grant_achievements
Función: check_and_grant_achievements.sql (Líneas 92-139)
-- AL DESBLOQUEAR (is_completed = true):
-- 1. Inserta en user_achievements
INSERT INTO gamification_system.user_achievements (
user_id, achievement_id, is_completed, completed_at, progress, max_progress
) VALUES (p_user_id, v_achievement.id, true, NOW(), 100, 100);
-- 2. Actualiza user_stats (XP + ML Coins)
UPDATE gamification_system.user_stats
SET
total_xp = COALESCE(total_xp, 0) + v_xp_reward,
ml_coins = v_new_balance,
achievements_earned = COALESCE(achievements_earned, 0) + 1
WHERE user_id = p_user_id;
-- 3. Registra transacción de coins
INSERT INTO gamification_system.ml_coins_transactions (...)
VALUES (...'earned_achievement'...'Logro desbloqueado: '...);
Función: claim_achievement_reward.sql (Líneas 54-95)
-- AL RECLAMAR (rewards_claimed = true):
-- 1. Actualiza user_achievements
UPDATE gamification_system.user_achievements
SET rewards_claimed = TRUE
WHERE user_id = p_user_id AND achievement_id = p_achievement_id;
-- 2. Actualiza user_stats (XP + ML Coins)
UPDATE gamification_system.user_stats
SET
total_xp = total_xp + COALESCE((v_achievement.rewards->>'xp')::INTEGER, v_achievement.points_value, 0),
ml_coins = v_new_balance
WHERE user_id = p_user_id;
-- 3. Registra transacción de coins
INSERT INTO gamification_system.ml_coins_transactions (...)
VALUES (...'earned_achievement'...'Recompensa reclamada: '...);
Tabla Comparativa
| Aspecto | check_and_grant | claim_achievement_reward |
|---|---|---|
| Cuándo se ejecuta | Al desbloquear logro | Al reclamar recompensa |
| Otorga XP | ✅ Sí | ✅ Sí |
| Otorga ML Coins | ✅ Sí | ✅ Sí |
| Registra transacción | ✅ Sí | ✅ Sí |
| Incrementa achievements_earned | ✅ Sí | ❌ No |
| Mensaje transacción | "Logro desbloqueado:" | "Recompensa reclamada:" |
⚠️ PROBLEMA CRÍTICO: Si ambas funciones se ejecutan para el mismo achievement, el usuario recibe DOBLE recompensa de XP y ML Coins.
7.2 Backend: achievements.service.ts - claimRewards() (Líneas 745-759)
async claimRewards(userId: string, achievementId: string): Promise<UserAchievement> {
const userAchievement = await this.checkProgress(userId, achievementId);
if (!userAchievement.is_completed) {
throw new BadRequestException(`Achievement ${achievementId} is not completed yet`);
}
if (userAchievement.rewards_claimed) {
throw new BadRequestException(`Rewards already claimed for achievement ${achievementId}`);
}
// ⚠️ PROBLEMA: Solo marca el flag, NO distribuye recompensas
userAchievement.rewards_claimed = true;
return this.userAchievementRepo.save(userAchievement);
// ❌ FALTA: No llama a claim_achievement_reward SQL
// ❌ FALTA: No llama a UserStatsService.addXp()
// ❌ FALTA: No llama a MLCoinsService.addCoins()
// ❌ FALTA: No registra transacción de coins
}
Contraste con otros servicios que SÍ distribuyen:
| Servicio | Distribuye XP | Distribuye Coins | Registra Trans. |
|---|---|---|---|
achievements.service.claimRewards() |
❌ NO | ❌ NO | ❌ NO |
exercise-rewards.service.claimRewards() |
✅ SÍ | ✅ SÍ | ✅ SÍ |
mission-claim.service.claimMission() |
✅ SÍ | ✅ SÍ | ✅ SÍ |
7.3 Frontend APIs: Comparación Detallada
achievementsAPI.ts - claimAchievementRewards() (Líneas 303-334)
export const claimAchievementRewards = async (
userId: string,
achievementId: string,
): Promise<{
success: boolean;
achievement_id: string;
rewards_claimed: boolean;
ml_coins_awarded?: number; // ⚠️ Espera valores que backend NO envía
xp_awarded?: number; // ⚠️ Espera valores que backend NO envía
}> => {
const { data } = await apiClient.post<ApiResponse<{...}>>(
`/gamification/users/${userId}/achievements/${achievementId}/claim`
);
return {
success: true,
achievement_id: achievementId,
rewards_claimed: data.data.rewards_claimed,
// ml_coins_awarded y xp_awarded NO vienen del backend
};
};
gamification.api.ts - claimAchievement() (Líneas 166-172)
claimAchievement: async (userId: string, achievementId: string): Promise<UserAchievement> => {
const { data } = await apiClient.post<UserAchievement>(
`/gamification/users/${userId}/achievements/${achievementId}/claim`,
{},
);
return data; // Retorna datos sin transformar
};
Diferencias Clave
| Aspecto | achievementsAPI.ts | gamification.api.ts |
|---|---|---|
| Ubicación | /features/gamification/social/api/ | /lib/api/ |
| Tipo retorno | Custom object | UserAchievement |
| Transformación | Inline | Sin transformar |
| Usado por | achievementsStore.ts | AchievementsPage.tsx |
| Manejo errores | handleAPIError() | throw directo |
7.4 Frontend Hook: /hooks/useAchievements.ts - Definiciones Hardcodeadas
⚠️ PROBLEMA MAYOR: Este hook tiene ~450 líneas de código con achievement definitions hardcodeadas:
// Líneas 71-250 - DEFINICIONES DUPLICADAS DEL BACKEND
const ACHIEVEMENT_DEFINITIONS: AchievementDefinition[] = [
{
id: 'first_steps',
title: 'Primeros Pasos',
description: 'Completa tu primer ejercicio',
icon: '👣',
rarity: 'common',
xp_reward: 10, // ⚠️ Puede no coincidir con backend
ml_coins_reward: 5, // ⚠️ Puede no coincidir con backend
condition: { type: 'exercises_completed', value: 1, operator: '>=' },
},
// ... 20+ más achievements hardcodeados
];
Problemas:
- Las recompensas pueden NO coincidir con la base de datos
- Nuevos achievements no aparecen hasta modificar código
- Difícil mantener sincronizado con seeds/backend
- Duplica lógica de detección que el backend ya tiene
7.5 Transformer: achievementTransformer.ts vs Inline Mappers
achievementTransformer.ts (Externo - CORRECTO)
// Líneas 212-266 - Bien estructurado
export const transformAchievement = (apiResponse: ApiAchievementResponse): Achievement => {
const rewards = {
xp: apiResponse.rewards?.xp ?? apiResponse.points_value ?? 0,
mlCoins: apiResponse.rewards?.ml_coins ?? apiResponse.ml_coins_reward ?? 0,
// ...
};
return {
id: apiResponse.id,
name: apiResponse.name,
// ... mapeo completo y consistente
};
};
achievementsAPI.ts (Inline - INCONSISTENTE)
// Líneas 382-411 - Duplica lógica del transformer
export const mapToFrontendAchievement = (
backendAchievement: BackendAchievement,
userProgress?: BackendUserAchievement,
): Achievement => {
return {
id: backendAchievement.id,
title: backendAchievement.name, // ⚠️ 'title' vs 'name' en transformer
// ... mapeo diferente
mlCoinsReward: backendAchievement.rewards?.ml_coins ?? backendAchievement.ml_coins_reward ?? 0,
xpReward: backendAchievement.rewards?.xp ?? backendAchievement.points_value ?? 0,
// ... campos diferentes
};
};
8. RESUMEN DE PROBLEMAS CONFIRMADOS
CRÍTICOS (Requieren corrección inmediata)
| ID | Problema | Impacto | Archivos |
|---|---|---|---|
| P-DUP-001 | Backend NO distribuye recompensas al claim | Users no reciben XP/Coins | achievements.service.ts |
| P-DUP-002 | SQL puede dar DOBLE recompensa | Inflación de economía | check_and_grant + claim_achievement |
| P-DUP-003 | Hook tiene 450+ líneas hardcoded | Desincronización con backend | /hooks/useAchievements.ts |
IMPORTANTES (Deben corregirse pronto)
| ID | Problema | Impacto | Archivos |
|---|---|---|---|
| P-DUP-004 | APIs duplicadas con diferente retorno | Confusión de desarrolladores | achievementsAPI.ts, gamification.api.ts |
| P-DUP-005 | Transformers inconsistentes | Datos transformados diferente | achievementsAPI.ts inline mappers |
MENORES (Mejoras de calidad)
| ID | Problema | Impacto | Archivos |
|---|---|---|---|
| P-DUP-006 | Schema mismatch en admin | Dashboard puede fallar | admin-progress.service.ts |
Analizado por: Claude (Arquitecto Técnico) Fecha: 2026-01-10 Estado: FASE 2 COMPLETADA - Pendiente FASE 3 (Planeación)