if (!lightbox.classList.contains('active')) return;
switch(e.key) {
case 'Escape': closeLightbox(); break;
case 'ArrowLeft': navigateLightbox(-1); break;
case 'ArrowRight': navigateLightbox(1); break;
case '+': case '=': zoomIn(); break;
case '-': zoomOut(); break;
case '0': resetZoom(); break;
}
});
const docs = {
"Inicio": {
"README": `# Sistema de Registro de Estudiantes\n\nSistema web para gestionar inscripciones de estudiantes en materias con restricciones de créditos y profesores.\n\n## Stack Tecnológico\n\n| Capa | Tecnología |\n|------|------------|\n| Backend | .NET 10, C# |\n| API | GraphQL (HotChocolate) |\n| Frontend | Angular 21, TypeScript |\n| Base de Datos | SQL Server 2022 |\n| ORM | Entity Framework Core |\n| UI | Angular Material |\n\n## Reglas de Negocio\n\n- 10 materias, cada una vale 3 créditos\n- 5 profesores, cada uno imparte 2 materias\n- Estudiantes pueden inscribir **máximo 3 materias** (9 créditos)\n- **Restricción:** Un estudiante NO puede tener materias con el mismo profesor\n\n## Características del Sistema\n\n### Funcionalidades\n- CRUD completo de estudiantes\n- Inscripción/cancelación de materias con validación de reglas\n- Visualización de compañeros de clase por materia\n- Interfaz responsive con Angular Material\n- **Sistema de autenticación con flujo de activación**\n- **Control de acceso por roles (Admin/Student)**\n\n### Calidad y Robustez\n- **Manejo de errores**: Mensajes amigables para usuarios + logging detallado para desarrolladores\n- **Monitoreo de conectividad**: Verificación cada 5 segundos con overlay bloqueante si se pierde conexión\n- **Validación**: FluentValidation en backend + validación reactiva en frontend\n- **Arquitectura**: Clean Architecture + CQRS + Ports & Adapters\n\n### DevOps\n- **Docker Compose**: Despliegue optimizado con un solo comando (\`./start.sh\`)\n- **Health Check**: Endpoint \`/health\` con verificación de base de datos\n- **Diagramas UML**: 8 diagramas PlantUML documentando la arquitectura\n\n## Requisitos Previos\n\n- [.NET 10 SDK](https://dotnet.microsoft.com/download)\n- [Node.js 22+](https://nodejs.org/)\n- [Docker](https://www.docker.com/) (para SQL Server)\n- Angular CLI 21: \`npm install -g @angular/cli\`\n\n## Inicio Rápido\n\n### Paso 1: Clonar e ir al directorio\n\n\`\`\`bash\ngit clone <repo-url>\ncd Interrapidisimo\n\`\`\`\n\n### Paso 2: Iniciar SQL Server con Docker\n\n\`\`\`bash\ndocker run -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=Asde71.4Asde71.4" \\\n -p 1433:1433 --name sqlserver -d mcr.microsoft.com/mssql/server:2022-latest\n\`\`\`\n\nEsperar ~10 segundos a que inicie completamente.\n\n### Paso 3: Configurar y ejecutar Backend\n\n\`\`\`bash\ncd src/backend\n\n# 1. Instalar herramienta EF Core (solo primera vez)\ndotnet tool install --global dotnet-ef\n# Si ya está instalada, ignorar el mensaje\n\n# 2. Restaurar dependencias\ndotnet restore\n\n# 3. Crear base de datos y aplicar migraciones\ndotnet ef database update -p Adapters/Driven/Persistence -s Host\n# NOTA: El mensaje "HostAbortedException" es NORMAL - indica que las migraciones se completaron\n\n# 4. Liberar puerto si está ocupado (opcional)\nfuser -k 5000/tcp 2>/dev/null\n\n# 5. Ejecutar API\ndotnet run --project Host\n\`\`\`\n\n**API GraphQL:** http://localhost:5000/graphql\n\n### Paso 4: Verificar que funciona\n\nEn otra terminal:\n\n\`\`\`bash\n# Consultar materias (debe retornar 10)\ncurl -s http://localhost:5000/graphql -X POST \\\n -H "Content-Type: application/json" \\\n -d '{"query":"{ subjects { id name credits } }"}' | jq .\n\n# Crear un estudiante\ncurl -s http://localhost:5000/graphql -X POST \\\n -H "Content-Type: application/json" \\\n -d '{"query":"mutation { createStudent(input: {name: \\"Test User\\", email: \\"test@example.com\\"}) { student { id name email } } }"}' | jq .\n\`\`\`\n\n### Paso 5: Frontend (opcional)\n\n\`\`\`bash\ncd src/frontend\n\n# Instalar dependencias (usar --legacy-peer-deps por compatibilidad)\nnpm install --legacy-peer-deps\n\n# Iniciar servidor de desarrollo\nnpx ng serve\n\`\`\`\n\n**App Angular:** http://localhost:4200\n\n> **Nota:** Si \`ng\` no está instalado globalmente, usar \`npx ng serve\`\n\n## Datos de Ejemplo\n\nLa migración incluye datos iniciales:\n\n| Profesor | Materias |\n|----------|----------|\n| Dr. García | Matemáticas I, Matemáticas II |\n| Dra. López | Física I, Física II |\n| Ing. Mar
"Entregables": `# Entregables - Prueba Técnica Senior .NET/Angular\n\n**Proyecto:** Sistema de Registro de Estudiantes\n**Empresa:** Inter Rapidísimo\n**Cargo:** Desarrollador Master .NET/Angular\n\n---\n\n## Resumen Ejecutivo\n\nSistema web completo para gestión de inscripciones de estudiantes con las siguientes características:\n\n- **Backend:** .NET 10, GraphQL (HotChocolate), Clean Architecture\n- **Frontend:** Angular 21, Standalone Components, Signals\n- **Base de Datos:** SQL Server con EF Core\n- **Seguridad:** OWASP compliant, rate limiting, query complexity\n- **Testing:** 145 tests automatizados\n- **Despliegue:** Docker + Kubernetes (k3s)\n\n---\n\n## Estructura del Proyecto\n\n\`\`\`\n/\n├── src/\n│ ├── backend/ # .NET 10 API GraphQL\n│ │ ├── Domain/ # Entidades, Value Objects, Ports\n│ │ ├── Application/ # Commands, Queries, DTOs\n│ │ ├── Adapters/ # GraphQL API, Persistence\n│ │ └── Host/ # Entry point, DI\n│ └── frontend/ # Angular 21 SPA\n├── tests/ # Tests automatizados\n├── docs/ # Documentación\n└── deploy/ # Docker + k3s\n\`\`\`\n\n---\n\n## Funcionalidades Implementadas\n\n### Requisitos Funcionales\n\n| # | Requisito | Estado |\n|---|-----------|--------|\n| 1 | CRUD completo de estudiantes | ✅ |\n| 2 | Inscripción en materias (max 3) | ✅ |\n| 3 | Visualización de compañeros de clase | ✅ |\n| 4 | Restricción de profesor único | ✅ |\n| 5 | 10 materias, 5 profesores | ✅ |\n| 6 | 3 créditos por materia | ✅ |\n| 7 | Validación de inscripciones | ✅ |\n| 8 | UI responsiva | ✅ |\n| 9 | Manejo de errores | ✅ |\n| 10 | Autenticación JWT | ✅ |\n| 11 | Flujo de activación de estudiantes | ✅ |\n| 12 | Control de acceso por roles (Admin/Student) | ✅ |\n\n### Reglas de Negocio\n\n- ✅ Máximo 3 materias por estudiante (9 créditos)\n- ✅ No repetir profesor en inscripciones\n- ✅ Validación en Domain Layer (pura, testeable)\n- ✅ Mensajes de error descriptivos\n\n### Sistema de Autenticación\n\n- ✅ **JWT** con HMAC-SHA256\n- ✅ **Flujo de Activación:** Admin crea estudiante → Código de activación → Estudiante activa cuenta\n- ✅ **Roles:** Admin (gestión completa) / Student (dashboard personal)\n- ✅ **Recuperación:** Código de recuperación generado en activación\n- ✅ **Seguridad:** PBKDF2-SHA256 para hashing de contraseñas\n\n---\n\n## Arquitectura\n\n### Clean Architecture\n\n\`\`\`\nHost → Adapters → Application → Domain\n\`\`\`\n\n- **Domain:** Entidades puras, sin dependencias\n- **Application:** Casos de uso con CQRS\n- **Adapters:** GraphQL API + EF Core\n- **Host:** Composición y DI\n\n### Patrones Implementados\n\n| Patrón | Uso |\n|--------|-----|\n| CQRS | Separación Commands/Queries |\n| Repository | Abstracción de persistencia |\n| Ports & Adapters | Inversión de dependencias |\n| DataLoader | Evitar N+1 en GraphQL |\n| Specification | Consultas reutilizables |\n\n### Diagramas de Arquitectura\n\nTodos los diagramas están disponibles en \`/docs/architecture/diagrams/\` en formatos PNG y SVG.\n\n| # | Diagrama | Archivo | Descripción |\n|---|----------|---------|-------------|\n| 1 | **Casos de Uso** | \`01-use-cases\` | Actores (Estudiante, Admin), funcionalidades del sistema, reglas de negocio |\n| 2 | **Modelo de Dominio** | \`02-domain-model\` | Entidades (User, Student, Professor, Subject, Enrollment), Value Objects, Domain Services |\n| 3 | **Secuencia Inscripción** | \`03-sequence-enrollment\` | Flujo completo de inscripción con JWT, validaciones y persistencia |\n| 4 | **Componentes** | \`04-components\` | Arquitectura Clean Architecture: Frontend Angular 21, Backend .NET 10, GraphQL |\n| 5 | **Entidad-Relación** | \`05-entity-relationship\` | Modelo de base de datos: tablas, relaciones, restricciones |\n| 6 | **Estados** | \`06-state-enrollment\` | Estados de cuenta (activación) e inscripciones (0-9 créditos) |\
"Prueba Técnica": `# PRUEBA TECNICA APLICACION WEB\n\n1. Los resultados deben ser enviados en un documento adjunto.\n2. Los entregables deben venir adjuntos.\n\n## El test consiste en:\n\nUna aplicación para registro de estudiantes:\n\n1. Realizar un CRUD que le permita a un usuario realizar un registro en línea.\n2. El estudiante se adhiere a un programa de créditos\n3. Existen 10 materias\n4. Cada materia equivale a 3 créditos.\n5. El estudiante sólo podrá seleccionar 3 materias.\n6. Hay 5 profesores que dictan 2 materias cada uno.\n7. El estudiante no podrá tener clases con el mismo profesor.\n8. Cada estudiante puede ver en línea los registros de otros estudiantes.\n9. El estudiante podrá ver sólo el nombre de los alumnos con quienes compartirá cada clase.\n\n## Entregables:\n\nUna aplicación web o Cliente servidor\nBase de datos o scripts para su creación en MySql / SQL\n`,
},
"Análisis": {
"AN-001 Requisitos Funcionales": `# AN-001: Análisis de Requisitos Funcionales\n\n**Proyecto:** Sistema de Registro de Estudiantes - Inter Rapidísimo\n**Rol:** Analista de Sistemas\n**Fecha:** 2026-01-07\n\n---\n\n## 1. Resumen Ejecutivo\n\nSistema web para gestión de inscripciones estudiantiles con programa de créditos académicos. Permite CRUD de estudiantes, inscripción en materias con restricciones de negocio, y visualización de compañeros de clase.\n\n---\n\n## 2. Requisitos Funcionales Identificados\n\n### RF-001: Registro de Estudiantes (CRUD)\n\n| Atributo | Descripción |\n|----------|-------------|\n| **ID** | RF-001 |\n| **Nombre** | Gestión CRUD de Estudiantes |\n| **Descripción** | El sistema debe permitir crear, leer, actualizar y eliminar registros de estudiantes |\n| **Prioridad** | Alta |\n| **Fuente** | Enunciado punto 1 |\n\n**Criterios de Aceptación:**\n- [ ] CA-001.1: Usuario puede crear estudiante con nombre y email válido\n- [ ] CA-001.2: Usuario puede consultar lista de estudiantes registrados\n- [ ] CA-001.3: Usuario puede actualizar datos de un estudiante existente\n- [ ] CA-001.4: Usuario puede eliminar un estudiante (cascade con inscripciones)\n- [ ] CA-001.5: Email debe ser único en el sistema\n\n---\n\n### RF-002: Programa de Créditos\n\n| Atributo | Descripción |\n|----------|-------------|\n| **ID** | RF-002 |\n| **Nombre** | Adhesión a Programa de Créditos |\n| **Descripción** | Los estudiantes se inscriben en un programa basado en créditos académicos |\n| **Prioridad** | Alta |\n| **Fuente** | Enunciado punto 2 |\n\n**Criterios de Aceptación:**\n- [ ] CA-002.1: Sistema muestra créditos totales del estudiante\n- [ ] CA-002.2: Créditos se calculan automáticamente según materias inscritas\n- [ ] CA-002.3: Máximo 9 créditos por estudiante (3 materias × 3 créditos)\n\n---\n\n### RF-003: Catálogo de Materias\n\n| Atributo | Descripción |\n|----------|-------------|\n| **ID** | RF-003 |\n| **Nombre** | Gestión de 10 Materias |\n| **Descripción** | El sistema debe gestionar un catálogo fijo de 10 materias académicas |\n| **Prioridad** | Alta |\n| **Fuente** | Enunciado punto 3 |\n\n**Criterios de Aceptación:**\n- [ ] CA-003.1: Sistema contiene exactamente 10 materias predefinidas\n- [ ] CA-003.2: Usuario puede consultar catálogo completo de materias\n- [ ] CA-003.3: Cada materia muestra nombre, créditos y profesor asignado\n\n---\n\n### RF-004: Valor de Créditos por Materia\n\n| Atributo | Descripción |\n|----------|-------------|\n| **ID** | RF-004 |\n| **Nombre** | Asignación de 3 Créditos por Materia |\n| **Descripción** | Cada materia del catálogo equivale a exactamente 3 créditos |\n| **Prioridad** | Alta |\n| **Fuente** | Enunciado punto 4 |\n\n**Criterios de Aceptación:**\n- [ ] CA-004.1: Todas las materias tienen valor fijo de 3 créditos\n- [ ] CA-004.2: Sistema muestra créditos en detalle de cada materia\n\n---\n\n### RF-005: Límite de Materias por Estudiante\n\n| Atributo | Descripción |\n|----------|-------------|\n| **ID** | RF-005 |\n| **Nombre** | Máximo 3 Materias por Estudiante |\n| **Descripción** | Un estudiante solo puede inscribirse en máximo 3 materias |\n| **Prioridad** | Alta |\n| **Fuente** | Enunciado punto 5 |\n\n**Criterios de Aceptación:**\n- [ ] CA-005.1: Sistema impide inscripción si estudiante ya tiene 3 materias\n- [ ] CA-005.2: Sistema muestra contador de materias inscritas\n- [ ] CA-005.3: Mensaje de error claro al intentar exceder límite\n\n---\n\n### RF-006: Asignación Profesores-Materias\n\n| Atributo | Descripción |\n|----------|-------------|\n| **ID** | RF-006 |\n| **Nombre** | 5 Profesores con 2 Materias c/u |\n| **Descripción** | El sistema tiene 5 profesores, cada uno dicta exactamente 2 materias |\n| **Prioridad** | Alta |\n| **Fuente** | Enunciado punto 6 |\n\n**Criterios de Aceptación:**\n- [ ] CA-006.1: Sistema contiene exactamente 5 profesores predefinidos\n- [ ] CA-006.2: Cada profesor tiene asignadas exactamente 2 materias\n- [ ] CA-006.3: Usuario puede ver qué profesor dict
"AN-002 Reglas de Negocio": `# AN-002: Reglas de Negocio\n\n**Proyecto:** Sistema de Registro de Estudiantes - Inter Rapidísimo\n**Rol:** Analista de Sistemas\n**Fecha:** 2026-01-07\n\n---\n\n## 1. Resumen\n\nEste documento define las reglas de negocio que gobiernan el sistema de registro de estudiantes. Estas reglas son invariantes y deben ser validadas en la capa de dominio.\n\n---\n\n## 2. Reglas de Negocio\n\n### RN-001: Estructura del Catálogo de Materias\n\n| Atributo | Valor |\n|----------|-------|\n| **ID** | RN-001 |\n| **Nombre** | Catálogo Fijo de Materias |\n| **Descripción** | El sistema contiene exactamente 10 materias académicas |\n| **Tipo** | Restricción Estructural |\n| **Severidad** | Crítica |\n\n**Datos Iniciales (Seed):**\n\n| ID | Materia | Profesor Asignado |\n|----|---------|-------------------|\n| 1 | Matemáticas I | Profesor A |\n| 2 | Matemáticas II | Profesor A |\n| 3 | Física I | Profesor B |\n| 4 | Física II | Profesor B |\n| 5 | Programación I | Profesor C |\n| 6 | Programación II | Profesor C |\n| 7 | Base de Datos I | Profesor D |\n| 8 | Base de Datos II | Profesor D |\n| 9 | Redes I | Profesor E |\n| 10 | Redes II | Profesor E |\n\n**Validación:** \`COUNT(Subjects) == 10\`\n\n---\n\n### RN-002: Valor Uniforme de Créditos\n\n| Atributo | Valor |\n|----------|-------|\n| **ID** | RN-002 |\n| **Nombre** | Créditos por Materia |\n| **Descripción** | Cada materia equivale exactamente a 3 créditos |\n| **Tipo** | Restricción de Valor |\n| **Severidad** | Crítica |\n\n**Fórmula:**\n\`\`\`\nCréditos_Materia = 3 (constante)\nCréditos_Estudiante = COUNT(Inscripciones) × 3\nCréditos_Máximos = 9 (3 materias × 3 créditos)\n\`\`\`\n\n**Validación:** \`Subject.Credits == 3\`\n\n---\n\n### RN-003: Límite de Inscripciones por Estudiante\n\n| Atributo | Valor |\n|----------|-------|\n| **ID** | RN-003 |\n| **Nombre** | Máximo 3 Materias |\n| **Descripción** | Un estudiante solo puede inscribirse en máximo 3 materias |\n| **Tipo** | Restricción de Cardinalidad |\n| **Severidad** | Crítica |\n\n**Escenarios:**\n\n| Materias Actuales | Acción | Resultado |\n|-------------------|--------|-----------|\n| 0-2 | Inscribir | ✓ Permitido |\n| 3 | Inscribir | ✗ Rechazado |\n| 1-3 | Cancelar | ✓ Permitido |\n\n**Validación:** \`Student.Enrollments.Count <= 3\`\n\n**Mensaje de Error:** "El estudiante ya tiene el máximo de 3 materias inscritas"\n\n---\n\n### RN-004: Asignación Profesores-Materias\n\n| Atributo | Valor |\n|----------|-------|\n| **ID** | RN-004 |\n| **Nombre** | Distribución de Profesores |\n| **Descripción** | Existen 5 profesores, cada uno dicta exactamente 2 materias |\n| **Tipo** | Restricción Estructural |\n| **Severidad** | Crítica |\n\n**Invariantes:**\n- \`COUNT(Professors) == 5\`\n- \`∀ Professor: COUNT(Professor.Subjects) == 2\`\n- \`∀ Subject: Subject.Professor != NULL\`\n\n**Validación:**\n\`\`\`csharp\nprofessors.All(p => p.Subjects.Count == 2)\nsubjects.All(s => s.ProfessorId != null)\n\`\`\`\n\n---\n\n### RN-005: Restricción de Profesor Único (CRÍTICA)\n\n| Atributo | Valor |\n|----------|-------|\n| **ID** | RN-005 |\n| **Nombre** | Prohibición de Mismo Profesor |\n| **Descripción** | Un estudiante NO puede inscribirse en dos materias dictadas por el mismo profesor |\n| **Tipo** | Restricción de Integridad |\n| **Severidad** | **CRÍTICA** |\n\n**Lógica de Validación:**\n\n\`\`\`\nPARA inscribir(estudiante, nuevaMateria):\n profesorNuevaMateria = nuevaMateria.Profesor\n profesoresActuales = estudiante.Inscripciones.Select(i => i.Materia.Profesor)\n\n SI profesorNuevaMateria EN profesoresActuales:\n RECHAZAR "Ya tienes una materia con este profesor"\n SINO:\n PERMITIR inscripción\n\`\`\`\n\n**Casos de Prueba:**\n\n| Estudiante tiene | Intenta inscribir | Resultado |\n|------------------|-------------------|-----------|\n| Matemáticas I (Prof A) | Matemáticas II (Prof A) | ✗ Rechazado |\n| Matemáticas I (Prof A) | Física I (Prof B) | ✓ Permitido |\n| Física I (Prof B), Programac
"AN-003 Historias de Usuario": `# AN-003: Historias de Usuario\n\n**Proyecto:** Sistema de Registro de Estudiantes - Inter Rapidísimo\n**Rol:** Product Owner\n**Fecha:** 2026-01-07\n\n---\n\n## 1. Épica Principal\n\n**EP-001: Sistema de Inscripción de Estudiantes**\n\nComo institución educativa, necesito un sistema web que permita a los estudiantes registrarse e inscribirse en materias siguiendo las reglas del programa de créditos, para gestionar eficientemente el proceso de matrícula.\n\n---\n\n## 2. Historias de Usuario\n\n### US-001: Registro de Estudiante\n\n| Campo | Valor |\n|-------|-------|\n| **ID** | US-001 |\n| **Épica** | EP-001 |\n| **Prioridad** | Alta |\n| **Story Points** | 5 |\n| **Sprint** | 1 |\n\n**Historia:**\n> Como **estudiante nuevo**,\n> quiero **registrarme en el sistema con mi nombre y correo electrónico**,\n> para **poder acceder al programa de inscripción de materias**.\n\n**Criterios de Aceptación:**\n\n\`\`\`gherkin\nScenario: Registro exitoso de estudiante\n Given estoy en la página de registro\n When ingreso un nombre válido "Juan Pérez"\n And ingreso un email válido "juan@email.com"\n And presiono el botón "Registrar"\n Then el sistema crea mi cuenta\n And veo un mensaje de confirmación\n And soy redirigido a la página de inscripción\n\nScenario: Registro con email duplicado\n Given existe un estudiante con email "juan@email.com"\n When intento registrarme con el mismo email\n Then veo el mensaje "Ya existe un estudiante con este email"\n And el formulario no se envía\n\nScenario: Registro con email inválido\n Given estoy en la página de registro\n When ingreso un email inválido "juanemail.com"\n Then veo el mensaje "Formato de email no válido"\n And el botón de registro está deshabilitado\n\`\`\`\n\n**Notas Técnicas:**\n- Validación de email en frontend y backend\n- Email almacenado en minúsculas (normalización)\n\n---\n\n### US-002: Consulta de Materias Disponibles\n\n| Campo | Valor |\n|-------|-------|\n| **ID** | US-002 |\n| **Épica** | EP-001 |\n| **Prioridad** | Alta |\n| **Story Points** | 3 |\n| **Sprint** | 1 |\n\n**Historia:**\n> Como **estudiante registrado**,\n> quiero **ver el catálogo de las 10 materias disponibles**,\n> para **conocer mis opciones de inscripción**.\n\n**Criterios de Aceptación:**\n\n\`\`\`gherkin\nScenario: Ver catálogo completo\n Given estoy autenticado como estudiante\n When accedo a la sección de materias\n Then veo una lista de 10 materias\n And cada materia muestra nombre, créditos (3) y profesor\n\nScenario: Ver detalle de materia\n Given estoy viendo el catálogo de materias\n When selecciono "Matemáticas I"\n Then veo el nombre del profesor asignado\n And veo que equivale a 3 créditos\n\`\`\`\n\n---\n\n### US-003: Inscripción en Materia\n\n| Campo | Valor |\n|-------|-------|\n| **ID** | US-003 |\n| **Épica** | EP-001 |\n| **Prioridad** | Alta |\n| **Story Points** | 8 |\n| **Sprint** | 1 |\n\n**Historia:**\n> Como **estudiante registrado**,\n> quiero **inscribirme en una materia disponible**,\n> para **acumular créditos en mi programa académico**.\n\n**Criterios de Aceptación:**\n\n\`\`\`gherkin\nScenario: Inscripción exitosa\n Given tengo menos de 3 materias inscritas\n And la materia "Física I" está disponible para mí\n When presiono "Inscribir" en "Física I"\n Then la materia se agrega a mis inscripciones\n And mis créditos aumentan en 3\n And veo mensaje de confirmación\n\nScenario: Intento inscripción con máximo alcanzado\n Given ya tengo 3 materias inscritas\n When intento inscribir otra materia\n Then veo el mensaje "Ya tienes el máximo de 3 materias"\n And el botón de inscripción está deshabilitado\n\nScenario: Intento inscripción con mismo profesor\n Given estoy inscrito en "Matemáticas I" (Profesor A)\n When intento inscribir "Matemáticas II" (Profesor A)\n Then veo el mensaje "Ya tienes una materia con este profesor"\n And la inscripción no se procesa\n\`\`\`\n\n**Notas Técnicas:**\n- Validaciones RN-003 y RN-005 en capa de do
"AN-004 Requisitos No Funcionales": `# AN-004: Requisitos No Funcionales\n\n**Proyecto:** Sistema de Registro de Estudiantes - Inter Rapidísimo\n**Rol:** Arquitecto de Software\n**Fecha:** 2026-01-07\n\n---\n\n## 1. Resumen\n\nEste documento define los atributos de calidad (requisitos no funcionales) que el sistema debe cumplir, estableciendo métricas medibles y criterios de aceptación.\n\n---\n\n## 2. Requisitos No Funcionales\n\n### RNF-001: Rendimiento - Tiempo de Respuesta\n\n| Atributo | Valor |\n|----------|-------|\n| **ID** | RNF-001 |\n| **Categoría** | Rendimiento |\n| **Prioridad** | Alta |\n| **Métrica** | Tiempo de respuesta <200ms(P95)|\n\n**Descripción:**\nElsistemadeberesponderalassolicitudesdelusuarioenmenosde200milisegundosenelpercentil95.\n\n**CriteriosdeAceptación:**\n\n|Operación|TiempoMáximo|\n|-----------|---------------|\n|Consultalistadeestudiantes|<150ms|\n|Consultacatálogomaterias|<100ms|\n|Inscripciónenmateria|<200ms|\n|Crear/actualizarestudiante|<200ms|\n|Consultacompañerosdeclase|<150ms|\n\n**Medición:**\n-Herramienta:ApplicationInsights/Logsestructurados\n-Ambiente:Producciónsimulada\n-Carga:100usuariosconcurrentes\n\n---\n\n###RNF-002:Rendimiento-Throughput\n\n|Atributo|Valor|\n|----------|-------|\n|**ID**|RNF-002|\n|**Categoría**|Rendimiento|\n|**Prioridad**|Media|\n|**Métrica**|>= 500 requests/segundo |\n\n**Descripción:**\nEl sistema debe soportar al menos 500 solicitudes por segundo sin degradación.\n\n**Criterios de Aceptación:**\n- [ ] API soporta 500 req/s con latencia <200ms\n-[]Sinerrores5xxbajocarganormal\n-[]CPU<80%bajocargamáxima\n\n---\n\n###RNF-003:Seguridad-OWASPTop10\n\n|Atributo|Valor|\n|----------|-------|\n|**ID**|RNF-003|\n|**Categoría**|Seguridad|\n|**Prioridad**|Crítica|\n|**Estándar**|OWASPTop102021|\n\n**Descripción:**\nElsistemadebeestarprotegidocontralas10vulnerabilidadesmáscríticassegúnOWASP.\n\n**ControlesporVulnerabilidad:**\n\n|#|Vulnerabilidad|ControlImplementado|\n|---|----------------|----------------------|\n|A01|BrokenAccessControl|Validacióndepermisosencadaendpoint|\n|A02|CryptographicFailures|HTTPSobligatorio,hashingdedatossensibles|\n|A03|Injection|Consultasparametrizadas(EFCore),validacióndeentrada|\n|A04|InsecureDesign|CleanArchitecture,validaciónendominio|\n|A05|SecurityMisconfiguration|Headersdeseguridad,CORSrestrictivo|\n|A06|VulnerableComponents|Auditoríadedependencias(\`dotnetlistpackage--vulnerable\`)|\n|A07|AuthFailures|Ratelimiting,validacióndesesión|\n|A08|DataIntegrityFailures|ValidacióndeDTOs,firmaderespuestas|\n|A09|LoggingFailures|Logsestructuradossindatossensibles|\n|A10|SSRF|ValidacióndeURLs,nofetchderecursosexternos|\n\n**CriteriosdeAceptación:**\n-[]AnálisisSASTsinvulnerabilidadescríticas\n-[]Headersdeseguridadconfigurados(CSP,X-Frame-Options,etc.)\n-[]Sinsecretosencódigofuente\n-[]HTTPSentodoslosendpoints\n\n---\n\n###RNF-004:Seguridad-ValidacióndeEntrada\n\n|Atributo|Valor|\n|----------|-------|\n|**ID**|RNF-004|\n|**Categoría**|Seguridad|\n|**Prioridad**|Alta|\n\n**Descripción:**\nTodaentradadelusuariodebeservalidadatantoenfrontendcomoenbackend.\n\n**ReglasdeValidación:**\n\n|Campo|ReglaFrontend|ReglaBackend|\n|-------|----------------|---------------|\n|Nombre|Required,MaxLength(100)|FluentValidation|\n|Email|Required,EmailFormat|ValueObject+Validator|\n|IDs|Numeric|Tipofuerte(int)|\n\n**CriteriosdeAceptación:**\n-[]NingúninputllegaalaBDsinvalidación\n-[]Mensajesdeerrorclarosysininformaciónsensible\n-[]SanitizacióndeHTMLencamposdetexto\n\n---\n\n###RNF-005:Usabilidad-ResponsiveDesign\n\n|Atributo|Valor|\n|----------|-------|\n|**ID**|RNF-005|\n|*
"AN-005 Riesgos Técnicos": `# AN-005: Análisis de Riesgos Técnicos\n\n**Proyecto:** Sistema de Registro de Estudiantes - Inter Rapidísimo\n**Rol:** Líder Técnico\n**Fecha:** 2026-01-07\n\n---\n\n## 1. Resumen\n\nIdentificación y evaluación de riesgos técnicos que podrían impactar el desarrollo, calidad o entrega del sistema. Cada riesgo incluye probabilidad, impacto y estrategia de mitigación.\n\n---\n\n## 2. Matriz de Evaluación\n\n| Probabilidad / Impacto | Bajo (1) | Medio (2) | Alto (3) |\n|------------------------|----------|-----------|----------|\n| **Alta (3)** | 3 | 6 | **9** |\n| **Media (2)** | 2 | 4 | 6 |\n| **Baja (1)** | 1 | 2 | 3 |\n\n**Clasificación:**\n- **Crítico:** 6-9 (acción inmediata)\n- **Moderado:** 3-5 (plan de mitigación)\n- **Bajo:** 1-2 (monitorear)\n\n---\n\n## 3. Riesgos Identificados\n\n### RT-001: Complejidad de Validaciones Cruzadas\n\n| Atributo | Valor |\n|----------|-------|\n| **ID** | RT-001 |\n| **Categoría** | Desarrollo |\n| **Probabilidad** | Alta (3) |\n| **Impacto** | Alto (3) |\n| **Score** | **9 - Crítico** |\n\n**Descripción:**\nLas validaciones de negocio (máximo 3 materias + no repetir profesor) requieren consultas cruzadas entre entidades. Una implementación incorrecta puede generar condiciones de carrera o validaciones inconsistentes.\n\n**Escenarios de Riesgo:**\n1. Dos inscripciones concurrentes del mismo estudiante\n2. Validación en frontend que no se replica en backend\n3. Race condition al verificar profesor duplicado\n\n**Estrategia de Mitigación:**\n\n| Acción | Responsable | Plazo |\n|--------|-------------|-------|\n| Implementar validaciones en Domain Service (única fuente de verdad) | Dev Senior | Sprint 1 |\n| Usar transacciones con nivel de aislamiento Serializable | Dev Senior | Sprint 1 |\n| Tests de concurrencia con múltiples threads | QA | Sprint 1 |\n| Lock optimista con RowVersion en Enrollment | Dev | Sprint 1 |\n\n**Código de Mitigación:**\n\`\`\`csharp\n// Transacción con locking\nawait using var transaction = await _context.Database\n .BeginTransactionAsync(IsolationLevel.Serializable);\n\nvar student = await _context.Students\n .Include(s => s.Enrollments)\n .ThenInclude(e => e.Subject)\n .FirstOrDefaultAsync(s => s.Id == command.StudentId);\n\n// Validaciones en dominio\n_enrollmentService.ValidateEnrollment(student, subject);\n\nawait _context.SaveChangesAsync();\nawait transaction.CommitAsync();\n\`\`\`\n\n---\n\n### RT-002: Integración Frontend-Backend (GraphQL)\n\n| Atributo | Valor |\n|----------|-------|\n| **ID** | RT-002 |\n| **Categoría** | Integración |\n| **Probabilidad** | Media (2) |\n| **Impacto** | Alto (3) |\n| **Score** | **6 - Crítico** |\n\n**Descripción:**\nGraphQL introduce complejidad adicional en la integración. Errores en el esquema, types incorrectos o problemas de N+1 queries pueden afectar rendimiento y desarrollo.\n\n**Escenarios de Riesgo:**\n1. Mismatch entre schema GraphQL y DTOs\n2. N+1 queries sin DataLoaders\n3. Errores de tipos en Apollo Angular\n4. Over-fetching o under-fetching de datos\n\n**Estrategia de Mitigación:**\n\n| Acción | Responsable | Plazo |\n|--------|-------------|-------|\n| Generar tipos TypeScript desde schema (codegen) | Dev Frontend | Sprint 1 |\n| Implementar DataLoaders para todas las relaciones | Dev Backend | Sprint 1 |\n| Configurar GraphQL Voyager para visualizar schema | Dev | Sprint 1 |\n| Tests de integración GraphQL con Banana Cake Pop | QA | Sprint 1 |\n\n**Configuración DataLoader:**\n\`\`\`csharp\n// DataLoader para evitar N+1\npublic class SubjectByIdDataLoader : BatchDataLoader<int,Subject>\n{\n protected override async Task<IReadOnlyDictionary<int,Subject>> LoadBatchAsync(\n IReadOnlyList<int> keys, CancellationToken ct)\n {\n return await _context.Subjects\n .Where(s => keys.Contains(s.Id))\n .ToDictionaryAsync(s => s.Id, ct);\n }\n}\n\`\`\`\n\n---\n\n### RT-003: Manejo de Concurrencia\n\n| Atributo | Valor |\n|----------|-------|\n| **ID** | RT-003 |\n| **Categor<6F>
},
"Diseño": {
"DI-001 Arquitectura Backend": `# DI-001: Arquitectura Backend\n\n**Proyecto:** Sistema de Registro de Estudiantes\n**Fecha:** 2026-01-07\n\n---\n\n## 1. Patrón: Clean Architecture + CQRS\n\n\`\`\`\n┌────────────────────────────────────────────────────────────┐\n│ HOST │\n│ (Program.cs, DI, Configuration) │\n└─────────────────────────┬──────────────────────────────────┘\n │\n┌─────────────────────────▼──────────────────────────────────┐\n│ ADAPTERS │\n│ ┌──────────────────────┬─────────────────────────────┐ │\n│ │ DRIVING (Primary) │ DRIVEN (Secondary) │ │\n│ │ ────────────────── │ ───────────────────── │ │\n│ │ GraphQL API │ Persistence (EF Core) │ │\n│ │ (HotChocolate) │ DataLoaders │ │\n│ └──────────────────────┴─────────────────────────────┘ │\n└─────────────────────────┬──────────────────────────────────┘\n │\n┌─────────────────────────▼──────────────────────────────────┐\n│ APPLICATION │\n│ Commands / Queries / Handlers / Validators │\n└─────────────────────────┬──────────────────────────────────┘\n │\n┌─────────────────────────▼──────────────────────────────────┐\n│ DOMAIN │\n│ Entities / Value Objects / Services / Ports │\n└────────────────────────────────────────────────────────────┘\n\`\`\`\n\n---\n\n## 2. Regla de Dependencia\n\n\`\`\`\nHost → Adapters → Application → Domain\n ↑\n Adapters implementa Ports\n\`\`\`\n\n**INVIOLABLE:** Domain NO depende de nada externo.\n\n---\n\n## 3. Responsabilidades por Capa\n\n| Capa | Responsabilidad | Tecnología |\n|------|-----------------|------------|\n| **Domain** | Entidades, Value Objects, reglas de negocio puras, Ports (interfaces) | C# puro |\n| **Application** | Casos de uso (Commands/Queries), DTOs, Validators, orquestación | MediatR, FluentValidation, Mapster |\n| **Adapters/Driving** | GraphQL API (Types, Resolvers, Mutations) | HotChocolate |\n| **Adapters/Driven** | Repositorios, DbContext, DataLoaders | EF Core, SQL Server |\n| **Host** | Composition Root, DI, Middleware | ASP.NET Core |\n\n---\n\n## 4. Estructura de Proyectos\n\n\`\`\`\nsrc/backend/\n├── Domain/\n│ ├── Entities/ # Student, Subject, Professor, Enrollment\n│ ├── ValueObjects/ # Email, Credits\n│ ├── Services/
"DI-002 Modelo de Dominio": `# DI-002: Modelo de Dominio\n\n**Proyecto:** Sistema de Registro de Estudiantes\n**Fecha:** 2026-01-07\n\n---\n\n## 1. Diagrama de Entidades\n\n\`\`\`\n┌─────────────────┐ ┌─────────────────────────┐\n│ PROFESSOR │ │ STUDENT │\n├─────────────────┤ ├─────────────────────────┤\n│ Id: int (PK) │ │ Id: int (PK) │\n│ Name: string │ │ Name: string │\n└────────┬────────┘ │ Email: Email │\n │ │ ActivationCodeHash? │ ← Nuevo\n │ 1:2 │ ActivationExpiresAt? │ ← Nuevo\n ▼ │ IsActivated (computed) │ ← Nuevo\n┌─────────────────┐ │ RowVersion │\n│ SUBJECT │ └────────┬────────────────┘\n├─────────────────┤ │\n│ Id: int (PK) │ │ 0..3\n│ Name: string │ ▼\n│ Credits: 3 │ ┌─────────────────┐\n│ ProfessorId: FK │◄──────│ ENROLLMENT │\n└─────────────────┘ 1:N ├─────────────────┤\n │ Id: int (PK) │\n┌─────────────────┐ │ StudentId: FK │\n│ USER │ │ SubjectId: FK │\n├─────────────────┤ │ EnrolledAt │\n│ Id: int (PK) │ └─────────────────┘\n│ Username │\n│ PasswordHash │\n│ RecoveryCodeHash│\n│ Role (Admin/ │\n│ Student) │\n│ StudentId?: FK │───────► 0..1 Student\n│ CreatedAt │\n│ LastLoginAt? │\n└─────────────────┘\n\`\`\`\n\n---\n\n## 2. Entidades\n\n### Student (Aggregate Root)\n\n\`\`\`csharp\npublic class Student\n{\n public const int MaxEnrollments = 3;\n\n public int Id { get; private set; }\n public string Name { get; private set; }\n public Email Email { get; private set; }\n\n // Campos de Activación (nuevo flujo)\n public string? ActivationCodeHash { get; private set; }\n public DateTime? ActivationExpiresAt { get; private set; }\n public bool IsActivated => ActivationCodeHash == null;\n\n private readonly List<Enrollment> _enrollments = new();\n public IReadOnlyCollection<Enrollment> Enrollments => _enrollments;\n\n public int TotalCredits => _enrollments.Count * 3;\n\n public void Enroll(Subject subject, IEnrollmentPolicy policy)\n {\n policy.Validate(this, subject);\n _enrollments.Add(new Enrollment(this, subject));\n }\n\n public void Unenroll(int subjectId)\n {\n var enrollment = _enrollments.FirstOrDefault(e => e.SubjectId == subjectId);\n if (enrollment != null) _enrollments.Remove(enrollment);\n }\n\n // Métodos de activación\n public void SetActivationCode(string codeHash, TimeSpan expiresIn)\n {\n ActivationCodeHash = codeHash;\n ActivationExpiresAt = DateTime.UtcNow.Add(expiresIn);\n }\n\n public void ClearActivationCode()\n {\n ActivationCodeHash = null;\n ActivationExpiresAt = null;\n }\n\n public bool IsActivationExpired() =>\n ActivationExpiresAt.HasValue && DateTime.UtcNow > ActivationExpiresAt.Value;\n}\n\`\`\`\n\n### User (Autenticación)\n\n\`\`\`csharp\npublic class User\n{\n public int Id { get; private set; }\n public string Username { get; private set; } // Almacenado en min<69>
"DI-003 Diseño Base de Datos": `# DI-003: Diseño de Base de Datos\n\n**Proyecto:** Sistema de Registro de Estudiantes\n**Fecha:** 2026-01-07\n\n---\n\n## 1. Modelo Entidad-Relación\n\n\`\`\`\n┌─────────────────────────────────────────────────────────────────┐\n│ PROFESSORS │\n├──────────────┬────────────────┬─────────────────────────────────┤\n│ Id │ INT │ PK, IDENTITY │\n│ Name │ NVARCHAR(100) │ NOT NULL │\n└──────────────┴────────────────┴─────────────────────────────────┘\n │\n │ 1:N (cada profesor → 2 materias)\n ▼\n┌─────────────────────────────────────────────────────────────────┐\n│ SUBJECTS │\n├──────────────┬────────────────┬─────────────────────────────────┤\n│ Id │ INT │ PK, IDENTITY │\n│ Name │ NVARCHAR(100) │ NOT NULL │\n│ Credits │ INT │ DEFAULT 3, CHECK (Credits = 3) │\n│ ProfessorId │ INT │ FK → Professors.Id │\n└──────────────┴────────────────┴─────────────────────────────────┘\n │\n │ 1:N\n ▼\n┌─────────────────────────────────────────────────────────────────┐\n│ ENROLLMENTS │\n├──────────────┬────────────────┬─────────────────────────────────┤\n│ Id │ INT │ PK, IDENTITY │\n│ StudentId │ INT │ FK → Students.Id │\n│ SubjectId │ INT │ FK → Subjects.Id │\n│ EnrolledAt │ DATETIME2 │ DEFAULT GETUTCDATE() │\n└──────────────┴────────────────┴─────────────────────────────────┘\n ▲\n │ 0..3:N\n │\n┌─────────────────────────────────────────────────────────────────┐\n│ STUDENTS │\n├──────────────┬────────────────┬─────────────────────────────────┤\n│ Id │ INT │ PK, IDENTITY │\n│ Name │ NVARCHAR(100) │ NOT NULL │\n│ Email │ NVARCHAR(255) │ NOT NULL, UNIQUE
"ADR-001 Clean Architecture": `# ADR-001: Clean Architecture\n\n**Estado:** Aceptado\n**Fecha:** 2026-01-07\n\n## Contexto\n\nNecesitamos una arquitectura que permita:\n- Testabilidad de reglas de negocio\n- Independencia de frameworks\n- Mantenibilidad a largo plazo\n- Separación clara de responsabilidades\n\n## Decisión\n\nAdoptar **Clean Architecture** con 4 capas: Domain, Application, Adapters, Host.\n\n\`\`\`\nHost → Adapters → Application → Domain\n\`\`\`\n\n## Consecuencias\n\n### Positivas\n- Domain sin dependencias externas (puro C#)\n- Reglas de negocio testeables sin mocks de infraestructura\n- Fácil cambiar ORM o base de datos\n- Fácil cambiar de REST a GraphQL (o viceversa)\n\n### Negativas\n- Mayor cantidad de archivos/proyectos\n- Curva de aprendizaje inicial\n- Overhead para proyectos muy pequeños\n\n## Alternativas Consideradas\n\n| Alternativa | Razón de Descarte |\n|-------------|-------------------|\n| N-Layer tradicional | Alto acoplamiento, difícil testear |\n| Vertical Slices | Menor separación de concerns |\n| Monolítico simple | No escala con complejidad |\n\n## Referencias\n\n- [Clean Architecture - Robert C. Martin](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)\n`,
"ADR-002 GraphQL vs REST": `# ADR-002: GraphQL vs REST\n\n**Estado:** Aceptado\n**Fecha:** 2026-01-07\n\n## Contexto\n\nLa aplicación tiene relaciones complejas:\n- Estudiantes → Inscripciones → Materias → Profesores\n- Consultas como "materias disponibles" requieren múltiples joins\n- Frontend necesita flexibilidad en datos solicitados\n\n## Decisión\n\nUsar **GraphQL** con HotChocolate como API.\n\n## Consecuencias\n\n### Positivas\n- **No over-fetching:** Cliente pide solo campos necesarios\n- **No under-fetching:** Una query obtiene datos relacionados\n- **Schema tipado:** Contrato explícito frontend-backend\n- **Playground incluido:** Banana Cake Pop para testing\n- **DataLoaders:** Resuelve N+1 automáticamente\n\n### Negativas\n- Complejidad adicional vs REST simple\n- Curva de aprendizaje GraphQL\n- Requiere configurar query complexity limits\n- Cache más complejo que HTTP caching\n\n## Ejemplo Comparativo\n\n### REST (múltiples requests)\n\`\`\`\nGET /students/1\nGET /students/1/enrollments\nGET /subjects/1\nGET /professors/1\n\`\`\`\n\n### GraphQL (una query)\n\`\`\`graphql\nquery {\n student(id: 1) {\n name\n enrollments {\n subject {\n name\n professor { name }\n }\n }\n }\n}\n\`\`\`\n\n## Alternativas\n\n| Alternativa | Razón de Descarte |\n|-------------|-------------------|\n| REST | Over/under-fetching, múltiples endpoints |\n| OData | Menos flexible, menos ecosistema |\n| gRPC | No ideal para frontend web |\n\n## Referencias\n\n- [HotChocolate Docs](https://chillicream.com/docs/hotchocolate)\n`,
"ADR-003 Angular Signals": `# ADR-003: Signals vs RxJS para Estado Local\n\n**Estado:** Aceptado\n**Fecha:** 2026-01-07\n\n## Contexto\n\nAngular 21 introduce Signals como alternativa a RxJS para estado reactivo.\nNecesitamos decidir el enfoque para manejo de estado en el frontend.\n\n## Decisión\n\nUsar **Signals para estado local** + **Apollo Client para estado del servidor**.\n\n\`\`\`typescript\n// Estado local con Signals\nstudents = signal<Student[]>([]);\nloading = signal(true);\n\n// Estado del servidor con Apollo\nthis.apollo.watchQuery<GetStudentsQuery>({...})\n\`\`\`\n\n## Consecuencias\n\n### Positivas\n- **Simplicidad:** Signals más intuitivos que BehaviorSubject\n- **Performance:** Integración nativa con OnPush\n- **Menos boilerplate:** No requiere async pipe en templates\n- **Type-safe:** Mejor inferencia de tipos\n\n### Negativas\n- Tecnología relativamente nueva\n- Menos operadores que RxJS\n- Apollo aún usa Observables internamente\n\n## Patrón Adoptado\n\n\`\`\`typescript\n@Component({\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class StudentListComponent {\n // Estado local\n students = signal<Student[]>([]);\n loading = signal(true);\n\n // Suscripción a Apollo\n ngOnInit() {\n this.studentService.getStudents().subscribe(({ data, loading }) => {\n this.students.set(data);\n this.loading.set(loading);\n });\n }\n}\n\`\`\`\n\n## Alternativas\n\n| Alternativa | Razón de Descarte |\n|-------------|-------------------|\n| RxJS puro | Mayor complejidad, más boilerplate |\n| NgRx | Overkill para esta aplicación |\n| Akita | Dependencia adicional innecesaria |\n\n## Referencias\n\n- [Angular Signals](https://angular.dev/guide/signals)\n`,
"ADR-004 Validation Strategy": `# ADR-004: Estrategia de Validación en 3 Capas\n\n**Estado:** Aceptado\n**Fecha:** 2026-01-07\n\n## Contexto\n\nLas reglas de negocio críticas son:\n- Máximo 3 materias por estudiante (9 créditos)\n- No repetir profesor en inscripciones\n- Datos de entrada válidos (email, nombre)\n\n## Decisión\n\nValidar en **3 capas** con responsabilidades distintas:\n\n| Capa | Responsabilidad | Tecnología |\n|------|-----------------|------------|\n| Frontend | UX, feedback rápido | Reactive Forms |\n| Application | Estructura de datos, sanitización | FluentValidation |\n| Domain | Reglas de negocio puras | Domain Services |\n\n## Implementación\n\n### Frontend (UX)\n\`\`\`typescript\nthis.form = this.fb.group({\n name: ['', [Validators.required, Validators.minLength(3)]],\n email: ['', [Validators.required, Validators.email]],\n});\n\`\`\`\n\n### Application (Sanitización + XSS)\n\`\`\`csharp\nRuleFor(x => x.Name)\n .NotEmpty()\n .Must(NotContainDangerousContent); // Previene XSS\n\`\`\`\n\n### Domain (Negocio)\n\`\`\`csharp\npublic void ValidateEnrollment(Student student, Subject subject)\n{\n if (student.Enrollments.Count >= 3)\n throw new MaxEnrollmentsExceededException();\n\n if (student.HasProfessor(subject.ProfessorId))\n throw new SameProfessorConstraintException();\n}\n\`\`\`\n\n## Consecuencias\n\n### Positivas\n- Defensa en profundidad\n- Separación de responsabilidades\n- UX mejorada (errores rápidos)\n- Seguridad garantizada (backend siempre valida)\n\n### Negativas\n- Duplicación parcial de reglas\n- Mantener sincronizadas las validaciones\n\n## Regla de Oro\n\n> **Nunca confíes en el frontend.** El backend SIEMPRE debe validar.\n\n## Referencias\n\n- [OWASP Input Validation](https://owasp.org/www-community/Input_Validation)\n`,
"Plan de Actividades": `# Plan de Actividades - Prueba Técnica Senior .NET/Angular\n\n## Información del Proyecto\n- **Cargo:** Desarrollador Master .NET/Angular\n- **Empresa:** Inter Rapidísimo\n- **Proyecto:** Sistema de Registro de Estudiantes\n- **Stack:** .NET 10 + GraphQL (HotChocolate) + Angular 21 + SQL Server\n\n---\n\n## Procesos de Fábrica de Software\n\n| Código | Proceso | Descripción |\n|--------|---------|-------------|\n| **AN** | Análisis | Levantamiento de requisitos, historias de usuario |\n| **DI** | Diseño | Arquitectura, modelos, prototipos UI/UX |\n| **DE** | Desarrollo | Codificación, implementación |\n| **QA** | Quality Assurance | Testing, revisión de código |\n| **DV** | DevOps | CI/CD, containerización, despliegue |\n| **DO** | Documentación | Técnica, usuario, API |\n| **SE** | Seguridad | Validaciones, autenticación, OWASP |\n\n---\n\n## Tabla de Actividades\n\n### Fase 1: Análisis y Planificación (AN)\n\n| # | Actividad | Detalle | Rol | Proceso |\n|---|-----------|---------|-----|---------|\n| 1.1 | Análisis de requisitos funcionales | Identificar y documentar los 9 requisitos del enunciado, criterios de aceptación por cada uno | Analista | AN |\n| 1.2 | Identificación de reglas de negocio | Documentar restricciones: máx 3 materias, 3 créditos/materia, 5 profesores con 2 materias c/u, restricción de profesor único | Analista | AN |\n| 1.3 | Definición de historias de usuario | Crear historias con formato "Como [rol] quiero [acción] para [beneficio]" con criterios de aceptación | Product Owner | AN |\n| 1.4 | Análisis de requisitos no funcionales | Definir: rendimiento (<200msrespuesta),seguridad(OWASPTop10),usabilidad(responsive),mantenibilidad|Arquitecto|AN|\n|1.5|Identificaciónderiesgostécnicos|Mapearriesgos:complejidaddevalidaciones,integraciónfrontend-backend,manejodeconcurrencia|LíderTécnico|AN|\n\n###Fase2:DiseñodeArquitectura(DI)\n\n|#|Actividad|Detalle|Rol|Proceso|\n|---|-----------|---------|-----|---------|\n|2.1|Diseñodearquitecturabackend|DefinirCleanArchitecture:Domain,Application,Infrastructure,GraphQL.Diagramadecapasydependencias|ArquitectoBackend|DI|\n|2.2|Diseñodelmodelodedominio|Creardiagramadeentidades:Student,Subject,Professor,Enrollment.Definiragregadosyvalueobjects|ArquitectoBackend|DI|\n|2.3|Diseñodebasededatos|ModeloE-Rnormalizado(3FN),índices,constraints,scriptsDDLconintegridadreferencial|DBA/Arquitecto|DI|\n|2.4|DiseñodeesquemaGraphQL|DefinirTypes,Queries,Mutations,Inputs,Payloads.DiseñarresolversyDataLoadersparaN+1|ArquitectoBackend|DI|\n|2.5|Diseñodearquitecturafrontend|DefinirestructuraAngular:standalonecomponents,signals,lazyloading,ApolloClientparaGraphQL|ArquitectoFrontend|DI|\n|2.6|DiseñodecomponentesUI|Wireframesdepantallas:listadoestudiantes,formularioinscripción,selecciónmaterias,vistacompañeros|UI/UXDesigner|DI|\n|2.7|DefinicióndecontratosGraphQL|SchemaGraphQLcompleto,DTOsderequest/response,interfacesdeservicios,contratosentrecapas|Arquitecto|DI|\n|2.8|Diseñodeestrategiademanejodeerrores|Definirexcepcionesdedominio,errorhandlingenGraphQL(Uniontypesparaerrores),respuestasestandarizadas|Arquitecto|DI|\n\n###Fase3:ConfiguracióndelEntorno(DV)\n\n|#|Actividad|Detalle|Rol|Proceso|\n|---|-----------|---------|-----|---------|\n|3.1|Inicializacióndelrepositorio|Crearestructuradecarpetas,.gitignore,README,DEV-GUIDE.mdconconvencionesdelproyecto|DevOps|DV|\n|3.2|Configuraciónsolución.NET|Crearsolucióncon4proyectos(Domain,Application,Infrastructure,GraphQL),referenciasentreproyectos|BackendDev|DV|\n|3.3|ConfiguraciónproyectoAngular|ngnewconstandalone,configurarESLint,Prettier,pathsaliases,ApolloAngularparaGraphQL|FrontendDev|DV|\n|3.4|Configuracióndebasede
"OWASP Checklist": `# OWASP Top 10 Security Checklist\n\n## Estado: Validado\n\n| # | Vulnerabilidad | Mitigación Implementada | Ubicación |\n|---|---------------|-------------------------|-----------|\n| A01 | **Broken Access Control** | No aplica (sin autenticación requerida) | N/A |\n| A02 | **Cryptographic Failures** | HTTPS forzado en producción (HSTS) | \`Program.cs:127\` |\n| A03 | **Injection** | FluentValidation + Regex sanitization, EF Core parameterized queries | \`CreateStudentValidator.cs\`, \`EnrollStudentValidator.cs\` |\n| A04 | **Insecure Design** | Clean Architecture, input validation en todas las capas | Arquitectura por capas |\n| A05 | **Security Misconfiguration** | Security headers (CSP, X-Frame-Options, etc.), Exception details disabled in prod | \`Program.cs:106-130\`, \`GraphQLExtensions.cs:53\` |\n| A06 | **Vulnerable Components** | Dependencias actualizadas (.NET 10, Angular 21) | \`*.csproj\`, \`package.json\` |\n| A07 | **Auth Failures** | No aplica (sin autenticación en este MVP) | N/A |\n| A08 | **Data Integrity Failures** | Input validation, FluentValidation, GraphQL type safety | Validators |\n| A09 | **Security Logging Failures** | Serilog structured logging, sensitive data filtering | \`appsettings.json:38-45\` |\n| A10 | **Server-Side Request Forgery** | No endpoints que acepten URLs externas | N/A |\n\n## Medidas de Seguridad Implementadas\n\n### Backend (.NET)\n\n1. **Input Validation**\n - FluentValidation con regex patterns\n - Sanitización de HTML/scripts\n - Longitud máxima de campos\n - Validación de formato email\n\n2. **Security Headers**\n - \`Content-Security-Policy\`\n - \`X-Content-Type-Options: nosniff\`\n - \`X-Frame-Options: DENY\`\n - \`Referrer-Policy: strict-origin-when-cross-origin\`\n - \`Permissions-Policy\`\n - \`Strict-Transport-Security\` (producción)\n\n3. **Rate Limiting**\n - 100 requests/minuto para queries GraphQL\n - 30 mutations/minuto\n - Queue limit para prevenir acumulación\n\n4. **GraphQL Security**\n - Query depth limit: 5 niveles\n - Query complexity limit: 100\n - Execution timeout: 30 segundos\n - Pagination max: 50 items\n\n5. **Logging Seguro**\n - Filtrado de datos sensibles (passwords, tokens)\n - Structured logging con Serilog\n - Rotación de logs (7 días)\n\n### Frontend (Angular)\n\n1. **XSS Prevention**\n - Angular sanitization por defecto\n - Content Security Policy\n\n2. **CSRF Protection**\n - No cookies de sesión (stateless GraphQL)\n\n3. **Secure Communication**\n - Solo HTTPS en producción\n - GraphQL sobre HTTPS\n\n## Pruebas de Seguridad Recomendadas\n\n\`\`\`bash\n# Test security headers\ncurl -I http://localhost:5000/graphql\n\n# Test rate limiting (debe retornar 429 después de 100 requests)\nfor i in {1..150}; do curl -s -o /dev/null -w "%{http_code}\\n" http://localhost:5000/graphql; done\n\n# Test query depth (debe fallar con depth > 5)\ncurl -X POST http://localhost:5000/graphql \\\n -H "Content-Type: application/json" \\\n -d '{"query":"{ students { enrollments { subject { professor { subjects { name } } } } } }"}'\n\`\`\`\n`,
"Recomendaciones": `# Recomendaciones Finales\n\n**Fecha:** 2026-01-08\n**Proyecto:** Sistema de Inscripción de Estudiantes\n**Versión:** 1.0\n\n---\n\n## Resumen del Estado Actual\n\nEl sistema cumple con todos los requisitos funcionales de la prueba técnica:\n\n| Requisito | Estado |\n|-----------|--------|\n| CRUD de estudiantes | ✅ Implementado |\n| Programa de créditos (10 materias, 3 créditos c/u) | ✅ Implementado |\n| Máximo 3 materias por estudiante | ✅ Implementado |\n| 5 profesores con 2 materias c/u | ✅ Implementado |\n| Restricción de mismo profesor | ✅ Implementado |\n| Ver compañeros de clase (solo nombres) | ✅ Implementado |\n| Autenticación y autorización | ✅ Implementado |\n| Recuperación de contraseña | ✅ Implementado |\n\n---\n\n## Recomendaciones Técnicas\n\n### 1. Seguridad\n\n#### Alta Prioridad\n- **Rate Limiting:** Implementar limitación de solicitudes en endpoints de autenticación para prevenir ataques de fuerza bruta.\n- **Refresh Tokens:** Actualmente solo se usa un token JWT. Implementar refresh tokens para mejor seguridad.\n- **Logging de Auditoría:** Agregar logs para acciones sensibles (login fallido, cambio de contraseña, etc.).\n\n#### Media Prioridad\n- **CORS Restrictivo:** Revisar configuración de CORS para producción (actualmente permite localhost).\n- **Helmet Headers:** Agregar headers de seguridad HTTP en producción.\n\n### 2. Rendimiento\n\n#### Alta Prioridad\n- **Paginación:** La query \`students\` debería usar paginación para escalabilidad.\n- **DataLoaders:** Ya implementados, pero verificar N+1 queries en GraphQL.\n\n#### Media Prioridad\n- **Caché de Apollo:** Optimizar políticas de caché en frontend para reducir llamadas al servidor.\n- **Compression:** Habilitar Brotli/gzip en nginx para assets estáticos.\n\n### 3. Calidad de Código\n\n#### Alta Prioridad\n- **Tests E2E:** Los tests de Playwright existen pero deben ejecutarse en CI/CD.\n- **Cobertura de Tests:** Aumentar cobertura en Domain y Application layers.\n\n#### Media Prioridad\n- **Error Handling Centralizado:** Crear interceptor global para manejo de errores GraphQL.\n- **Typing Estricto:** Generar tipos TypeScript desde el schema GraphQL automáticamente.\n\n### 4. DevOps\n\n#### Alta Prioridad\n- **Health Checks:** Mejorar endpoint \`/health\` para incluir dependencias externas.\n- **Secrets Management:** No hardcodear credenciales en manifiestos de k8s (usar Sealed Secrets o Vault).\n\n#### Media Prioridad\n- **Monitoring:** Agregar métricas con Prometheus y dashboards en Grafana.\n- **Logging Centralizado:** Configurar stack ELK o Loki para logs.\n\n---\n\n## Mejoras Funcionales Sugeridas\n\n### Corto Plazo (Sprint actual)\n1. **Confirmación de Cancelación:** Agregar diálogo de confirmación antes de desinscribir materia.\n2. **Notificaciones Push:** Informar a estudiantes cuando un compañero se inscribe en su clase.\n3. **Validación de Email:** Agregar validación de formato de email en frontend.\n\n### Mediano Plazo (2-4 sprints)\n1. **Horarios:** Agregar horarios a materias para evitar conflictos.\n2. **Waitlist:** Implementar lista de espera para materias muy demandadas.\n3. **Reportes:** Dashboard administrativo con métricas de inscripciones.\n\n### Largo Plazo (Roadmap)\n1. **Multi-tenant:** Soporte para múltiples instituciones.\n2. **Integración LMS:** Conectar con sistemas de gestión de aprendizaje.\n3. **App Mobile:** Versión móvil nativa con Flutter/React Native.\n\n---\n\n## Arquitectura\n\n### Fortalezas Actuales\n- **Clean Architecture:** Separación clara de capas (Domain, Application, Adapters).\n- **CQRS:** Comandos y queries bien separados con MediatR.\n- **GraphQL:** API flexible con HotChocolate.\n- **Angular Signals:** Estado reactivo moderno y eficiente.\n\n### Áreas de Mejora\n1. **Event Sourcing:** Considerar para auditoría completa de inscripciones.\n2. **SAGA Pattern:** Para operaciones distribuidas (si se escala a microservicios).\n3. **API Gateway:** Si se agregan más servicios, usar Kong o Traefik.\n\n---\n\n##
"Defectos QA": `# Reporte de Pruebas Manuales QA\n\n**Fecha:** 2026-01-08\n**Tester:** QA Team\n**Ambiente:** Desarrollo Local (localhost:4200 / localhost:5000)\n\n---\n\n## Resumen Ejecutivo\n\n| Categoría | Total | Pasaron | Fallaron | Corregidos |\n|-----------|-------|---------|----------|------------|\n| CRUD Estudiantes | 4 | 4 | 0 | 1 |\n| Inscripciones | 5 | 5 | 0 | 0 |\n| Compañeros | 2 | 2 | 0 | 0 |\n| **Total** | **11** | **11** | **0** | **1** |\n\n---\n\n## Capturas de Pantalla\n\n| # | Archivo | Descripción |\n|---|---------|-------------|\n| 01 | \`01-inicio-cargando.png\` | Página inicial cargando |\n| 02 | \`02-formulario-nuevo-estudiante.png\` | Formulario de nuevo estudiante |\n| 03 | \`03-formulario-lleno.png\` | Formulario con datos |\n| 04 | \`04-estudiante-creado.png\` | Estudiante creado exitosamente |\n| 05 | \`05-DEFECTO-editar-estudiante-no-encontrado.png\` | **DEFECTO: Error al editar** |\n| 06 | \`06-pagina-inscripcion.png\` | Página de inscripción |\n| 07 | \`07-inscripcion-exitosa-regla-profesor.png\` | Regla de profesor funcionando |\n| 08 | \`08-maximo-3-materias.png\` | Límite de materias visible |\n| 09 | \`09-creditos-maximos-9-9.png\` | 9/9 créditos alcanzados |\n| 10 | \`10-dos-estudiantes.png\` | Lista con dos estudiantes |\n| 11 | \`11-companeros-de-clase.png\` | Vista de compañeros |\n| 12 | \`12-confirmar-eliminacion.png\` | Diálogo de confirmación |\n| 13 | \`13-estudiante-eliminado.png\` | Estudiante eliminado |\n| 14 | \`14-CORREGIDO-editar-estudiante-funciona.png\` | **CORREGIDO: Editar funciona** |\n\n---\n\n## Defectos Encontrados\n\n### DEFECTO #1: Error "Estudiante no encontrado" al editar\n\n**ID:** DEF-001\n**Severidad:** Alta\n**Prioridad:** P1\n**Estado:** RESUELTO\n\n#### Descripcion\nAl hacer clic en el boton de editar (icono de lapiz) en la lista de estudiantes, aparece un snackbar con el mensaje "Estudiante no encontrado" en lugar de abrir el formulario de edicion.\n\n#### Pasos para Reproducir\n1. Ir a http://localhost:4200/students\n2. Crear un nuevo estudiante\n3. En la tabla, hacer clic en el boton de editar (icono de lapiz)\n4. **Resultado (antes):** Aparecia mensaje "Estudiante no encontrado"\n5. **Resultado (despues del fix):** El formulario de edicion abre correctamente\n\n#### Capturas de Pantalla\n| Estado | Captura |\n|--------|---------|\n| Antes |  |\n| Despues |  |\n\n#### Causa Raiz Identificada\nSe encontraron **DOS problemas** combinados:\n\n1. **Problema de navegacion:** El boton de editar usaba \`<button[routerLink]>\` pero en Angular el \`routerLink\` funciona mejor con elementos \`<a>\`.\n\n2. **Problema de timing con signals:** El componente \`StudentFormComponent\` usaba \`ngOnInit\` para leer el parametro de ruta \`id\` via \`input()\` signal, pero el valor no estaba disponible en ese momento del ciclo de vida.\n\n#### Archivos Modificados\n- \`student-list.component.ts\`: Cambio \`<button>\` por \`<a>\` para el boton de editar\n- \`student-form.component.ts\`: Cambio \`ngOnInit\` por \`effect()\` para reaccionar cuando el input signal este disponible\n\n---\n\n## Analisis de Soluciones - DEFECTO #1\n\n### Soluciones Propuestas\n\n| Solucion | Descripcion | Ventajas | Desventajas |\n|----------|-------------|----------|-------------|\n| A | Verificar navegacion y parametros de ruta | Simple si el problema es routing | No resuelve si es backend |\n| B | Revisar servicio de estudiantes | Identifica problemas en servicios | Requiere debugging profundo |\n| C | Agregar logs de diagnostico | Ayuda a identificar donde falla | Es temporal |\n| D | Usar modal en lugar de navegacion | Evita problemas de nav, mejor UX | Cambio arquitectonico grande |\n\n### SOLUCION IMPLEMENTADA\n\nSe implemento una **combinacion de A + investigacion profunda** que revelo dos problemas:\n\n#### Fix 1: Cambiar \`<button>\` por \`<a>\` en student-list.component.ts\n\n\`\`\`typescript\n// ANTES