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
296 lines
8.5 KiB
Markdown
296 lines
8.5 KiB
Markdown
# 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:**
|
|
|
|
```sql
|
|
-- 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):**
|
|
```sql
|
|
-- 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):**
|
|
```sql
|
|
-- 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:**
|
|
```typescript
|
|
// 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:**
|
|
```typescript
|
|
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):**
|
|
```typescript
|
|
// 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):**
|
|
```typescript
|
|
// 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:**
|
|
```sql
|
|
-- 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:**
|
|
```typescript
|
|
// 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:**
|
|
```typescript
|
|
// 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:**
|
|
```sql
|
|
-- Ejemplo correcto de seed:
|
|
conditions = '{"type": "exercise_completion", "requirements": {"exercises_completed": 1}}'::jsonb
|
|
|
|
-- Estructura esperada por backend: ✅ CORRECTA
|
|
```
|
|
|
|
**Estructura de rewards verificada:**
|
|
```sql
|
|
-- 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:**
|
|
1. ✅ Fase A: Corregir función SQL
|
|
2. ✅ Fase B: Corregir Store con nullish coalescing
|
|
3. ✅ Fase C: Expandir mapeo de categorías
|
|
4. ⏳ Fase D: Validar seeds (ya verificado - correcto)
|
|
5. ⏳ Fase E: Testing end-to-end
|
|
|
|
---
|
|
|
|
**Validado por:** Claude (Arquitecto Técnico)
|
|
**Fecha:** 2026-01-10
|
|
**Siguiente Fase:** Refinamiento del plan → Ejecución
|