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
9.8 KiB
FASE 3: PLAN DE CORRECCIÓN - DUPLICADOS ACHIEVEMENTS SYSTEM
Fecha: 2026-01-10 Proyecto: Gamilit Basado en: ANALISIS-DUPLICADOS-ACHIEVEMENTS-2026-01-10.md Estado: EN PLANEACIÓN
1. DECISIONES ARQUITECTÓNICAS
1.1 Modelo de Distribución de Recompensas
DECISIÓN: Implementar modelo de Claim-to-Earn (reclamar para ganar)
| Acción | Qué sucede | Quién distribuye |
|---|---|---|
| Achievement desbloqueado | Solo marca is_completed = true |
Backend service |
| Achievement reclamado | Distribuye XP + ML Coins | SQL function |
Justificación:
- Permite UX de "reclamar recompensa" (más satisfactorio)
- Evita dar recompensas automáticas si usuario no las ve
- Consistent con misiones y ejercicios que también usan "claim"
1.2 API Unificada
DECISIÓN: Mantener gamification.api.ts como API principal, deprecar duplicados en achievementsAPI.ts
Justificación:
gamification.api.tsya usa transformers externos (mejor mantenibilidad)- Consolidar en
/lib/api/para acceso global achievementsAPI.tstiene mappers inline que duplican transformer
1.3 Hook de Definiciones
DECISIÓN: Deprecar /hooks/useAchievements.ts (450 líneas hardcodeadas)
Justificación:
- Las definiciones deben venir del backend
- El hook
/features/gamification/social/hooks/useAchievements.tsusa el store correctamente - La detección automática la debe hacer el backend (ya implementada en
detectAndGrantEarned)
2. PLAN DE CORRECCIÓN POR PROBLEMA
2.1 P-DUP-001: Backend NO Distribuye Recompensas
Severidad: 🔴 CRÍTICO
Archivo a modificar: /apps/backend/src/modules/gamification/services/achievements.service.ts
Cambio propuesto: Llamar a función SQL claim_achievement_reward en lugar de solo marcar flag
// ANTES (líneas 745-759):
async claimRewards(userId: string, achievementId: string): Promise<UserAchievement> {
const userAchievement = await this.checkProgress(userId, achievementId);
// validaciones...
userAchievement.rewards_claimed = true;
return this.userAchievementRepo.save(userAchievement);
}
// DESPUÉS:
async claimRewards(userId: string, achievementId: string): Promise<{
userAchievement: UserAchievement;
xp_granted: number;
coins_granted: number;
}> {
// Llamar función SQL que distribuye recompensas
const result = await this.dataSource.query(
`SELECT * FROM gamification_system.claim_achievement_reward($1, $2)`,
[userId, achievementId]
);
if (!result[0].success) {
throw new BadRequestException(result[0].message);
}
// Obtener userAchievement actualizado
const userAchievement = await this.checkProgress(userId, achievementId);
return {
userAchievement,
xp_granted: result[0].xp_granted,
coins_granted: result[0].coins_granted,
};
}
Dependencias:
- Función SQL
claim_achievement_rewarddebe estar actualizada (✅ Ya corregida) - Controller debe actualizarse para manejar nuevo retorno
2.2 P-DUP-002: SQL Puede Dar DOBLE Recompensa
Severidad: 🔴 CRÍTICO
Archivos a modificar:
/apps/database/ddl/schemas/gamification_system/functions/check_and_award_achievements.sql
Cambio propuesto: Remover distribución de recompensas de check_and_grant_achievements - solo debe marcar is_completed, no dar XP/Coins
-- ANTES (líneas 111-118):
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,
updated_at = NOW()
WHERE user_id = p_user_id;
-- Registrar transaccion de coins
INSERT INTO gamification_system.ml_coins_transactions (...)
-- DESPUÉS:
UPDATE gamification_system.user_stats
SET
achievements_earned = COALESCE(achievements_earned, 0) + 1,
updated_at = NOW()
WHERE user_id = p_user_id;
-- NO registrar transacción - se hará en claim_achievement_reward
Impacto:
- El desbloqueo automático ya no dará recompensas
- Las recompensas solo se dan al reclamar
2.3 P-DUP-003: Hook con Definiciones Hardcodeadas
Severidad: 🔴 CRÍTICO
Archivo a deprecar: /apps/frontend/src/hooks/useAchievements.ts
Acción: Agregar comentario de deprecación y redireccionar a hook correcto
/**
* @deprecated Use useAchievements from '@/features/gamification/social/hooks/useAchievements'
* Este hook contiene definiciones hardcodeadas que están desactualizadas.
* La detección de achievements se hace en el backend (achievements.service.detectAndGrantEarned)
*/
Alternativa: Si se necesita auto-detection en frontend, llamar endpoint:
// En lugar de polling local, usar endpoint del backend
const result = await apiClient.post(`/gamification/users/${userId}/achievements/detect`);
2.4 P-DUP-004: APIs Duplicadas
Severidad: 🟡 IMPORTANTE
Archivo a modificar: /apps/frontend/src/features/gamification/social/api/achievementsAPI.ts
Cambio propuesto: Deprecar métodos duplicados y re-exportar desde gamification.api.ts
// ANTES:
export const claimAchievementRewards = async (...) => { ... };
// DESPUÉS:
/**
* @deprecated Use gamificationApi.claimAchievement from '@/lib/api/gamification.api'
*/
export const claimAchievementRewards = gamificationApi.claimAchievement;
Archivos que consumen achievementsAPI.ts:
achievementsStore.ts- Actualizar imports
2.5 P-DUP-005: Transformers Inconsistentes
Severidad: 🟡 IMPORTANTE
Archivo a modificar: /apps/frontend/src/features/gamification/social/api/achievementsAPI.ts
Cambio propuesto: Remover mappers inline y usar transformer externo
// ANTES (líneas 382-411):
export const mapToFrontendAchievement = (...) => { ... };
// DESPUÉS:
import { transformAchievement, transformUserAchievement } from
'@/features/gamification/achievements/utils/achievementTransformer';
// Re-export para compatibilidad
export { transformAchievement as mapToFrontendAchievement };
2.6 P-DUP-006: Schema Mismatch Admin
Severidad: 🟢 MENOR
Archivo a modificar: /apps/backend/src/modules/admin/services/admin-progress.service.ts
Cambio propuesto: Actualizar query SQL para usar campos correctos
-- ANTES:
SELECT
ua.id, ua.achievement_id, a.name, a.description, a.category, a.tier,
a.xp_reward, a.ml_coins_reward, a.icon_url,
ua.unlocked_at, ua.progress_current, ua.progress_required
-- DESPUÉS:
SELECT
ua.id, ua.achievement_id, a.name, a.description, a.category,
a.difficulty_level as tier, -- Mapear difficulty_level a tier
COALESCE((a.rewards->>'xp')::INTEGER, a.points_value, 0) as xp_reward,
a.ml_coins_reward, a.icon as icon_url,
ua.completed_at as unlocked_at, -- Usar completed_at
ua.progress as progress_current, -- Usar progress
ua.max_progress as progress_required -- Usar max_progress
3. ORDEN DE EJECUCIÓN
Fase A: Correcciones SQL (Base de datos)
| Orden | Archivo | Cambio | Dependencias |
|---|---|---|---|
| A.1 | check_and_award_achievements.sql |
Remover distribución de rewards | Ninguna |
Fase B: Correcciones Backend (NestJS)
| Orden | Archivo | Cambio | Dependencias |
|---|---|---|---|
| B.1 | achievements.service.ts |
Llamar SQL function en claimRewards | A.1 completado |
| B.2 | achievements.controller.ts |
Actualizar response type | B.1 completado |
| B.3 | admin-progress.service.ts |
Fix schema mismatch | Ninguna |
Fase C: Correcciones Frontend (React)
| Orden | Archivo | Cambio | Dependencias |
|---|---|---|---|
| C.1 | achievementsAPI.ts |
Deprecar y usar transformer | Ninguna |
| C.2 | achievementsStore.ts |
Actualizar imports | C.1 completado |
| C.3 | /hooks/useAchievements.ts |
Agregar deprecation notice | Ninguna |
4. VALIDACIONES REQUERIDAS
4.1 Antes de Ejecutar
- Backup de base de datos
- Verificar que no hay usuarios en proceso de claim
- Tests existentes pasan
4.2 Durante Ejecución
- Cada cambio SQL aplicado con
\dfpara verificar - Build de backend exitoso después de cada cambio
- Build de frontend exitoso después de cada cambio
4.3 Después de Ejecutar
- Test E2E: Usuario desbloquea achievement → NO recibe rewards automático
- Test E2E: Usuario reclama achievement → SÍ recibe rewards
- Test: Intentar reclamar dos veces → Error apropiado
- Test: Dashboard admin muestra datos correctos
5. ROLLBACK PLAN
Si falla Fase A (SQL):
-- Restaurar check_and_award_achievements.sql desde git
git checkout HEAD~1 -- apps/database/ddl/schemas/gamification_system/functions/check_and_award_achievements.sql
-- Re-ejecutar script de creación de función
Si falla Fase B (Backend):
# Restaurar archivos modificados
git checkout HEAD~1 -- apps/backend/src/modules/gamification/services/achievements.service.ts
git checkout HEAD~1 -- apps/backend/src/modules/gamification/controllers/achievements.controller.ts
npm run build
Si falla Fase C (Frontend):
# Restaurar archivos modificados
git checkout HEAD~1 -- apps/frontend/src/features/gamification/social/
npm run build
6. ESTIMACIÓN DE IMPACTO
| Métrica | Antes | Después |
|---|---|---|
| Archivos modificados | - | 7 |
| Líneas cambiadas | - | ~150 |
| Funciones deprecadas | - | 3 |
| Tests requeridos | - | 4 E2E |
7. DOCUMENTOS RELACIONADOS
| Documento | Estado |
|---|---|
| ANALISIS-DUPLICADOS-ACHIEVEMENTS-2026-01-10.md | ✅ Completado |
| PLAN-DUPLICADOS-ACHIEVEMENTS-2026-01-10.md | ✅ Actual |
| VALIDACION-PLAN-DUPLICADOS-2026-01-10.md | ⏳ Pendiente |
Planeado por: Claude (Arquitecto Técnico) Fecha: 2026-01-10 Estado: FASE 3 COMPLETADA - Pendiente FASE 4 (Validación)