# ⚠️ Errores Críticos Médicos - Sistema de Bloqueo ## 🎯 Objetivo Definir qué errores médicos son críticos y deben bloquear acciones en la aplicación. --- ## 🚨 Errores Críticos que Bloquean Acciones ### 1. Errores de Dosis #### ❌ BLOQUEA: Dosis fuera de rango seguro - **Condición:** Dosis < mínimo terapéutico O dosis > máximo seguro - **Acción:** Bloquear administración, mostrar error crítico - **Ejemplo:** Adrenalina 10mg IV (máximo seguro: 1mg) #### ❌ BLOQUEA: Dosis letal - **Condición:** Dosis que puede causar muerte o daño grave - **Acción:** Bloquear completamente, alerta de emergencia - **Ejemplo:** Adrenalina 1:1000 IV en lugar de 1:10.000 #### ❌ BLOQUEA: Vía de administración incorrecta - **Condición:** Vía no indicada para el fármaco - **Acción:** Bloquear administración - **Ejemplo:** Adrenalina IM en lugar de IV para PCR #### ⚠️ ADVIERTE (no bloquea): Dosis en límites - **Condición:** Dosis en límite superior/inferior del rango - **Acción:** Mostrar advertencia, requerir confirmación - **Ejemplo:** Dosis máxima recomendada --- ### 2. Errores de Protocolos #### ❌ BLOQUEA: Paso crítico omitido - **Condición:** Paso marcado como crítico no ejecutado - **Acción:** Bloquear continuación del protocolo - **Ejemplo:** No iniciar compresiones en RCP #### ❌ BLOQUEA: Orden de pasos incorrecto - **Condición:** Pasos ejecutados fuera de orden crítico - **Acción:** Bloquear continuación, requerir corrección - **Ejemplo:** Desfibrilar antes de iniciar compresiones #### ❌ BLOQUEA: Prerequisito no cumplido - **Condición:** Protocolo prerequisito no ejecutado - **Acción:** Bloquear inicio del protocolo - **Ejemplo:** Intentar SVA sin haber iniciado SVB #### ⚠️ ADVIERTE (no bloquea): Paso opcional omitido - **Condición:** Paso opcional no ejecutado - **Acción:** Mostrar advertencia, permitir continuar - **Ejemplo:** No usar cánula orofaríngea si no está disponible --- ### 3. Errores de Contenido Médico #### ❌ BLOQUEA: Contenido no validado médicamente - **Condición:** Intentar publicar contenido sin validación médica - **Acción:** Bloquear publicación - **Ejemplo:** Publicar protocolo sin revisión médica #### ❌ BLOQUEA: Contenido con errores críticos identificados - **Condición:** Contenido rechazado por revisor médico con errores críticos - **Acción:** Bloquear publicación hasta corrección - **Ejemplo:** Dosis incorrectas en vademécum #### ⚠️ ADVIERTE (no bloquea): Contenido desactualizado - **Condición:** Contenido sin actualizar >2 años - **Acción:** Mostrar advertencia de desactualización - **Ejemplo:** Protocolo basado en guías antiguas --- ### 4. Errores de Paciente/Contexto #### ❌ BLOQUEA: Peso inválido para edad - **Condición:** Peso fuera de rango fisiológico para edad - **Acción:** Bloquear cálculo de dosis, requerir verificación - **Ejemplo:** Niño de 5 años con peso de 100kg #### ❌ BLOQUEA: Edad incompatible con protocolo - **Condición:** Protocolo no aplicable a grupo de edad - **Acción:** Bloquear ejecución del protocolo - **Ejemplo:** Protocolo pediátrico aplicado a adulto #### ⚠️ ADVIERTE (no bloquea): Peso fuera de percentiles normales - **Condición:** Peso fuera de percentiles 5-95 para edad - **Acción:** Mostrar advertencia, permitir continuar con confirmación - **Ejemplo:** Peso muy bajo o muy alto pero posible --- ## 🏗️ Implementación ### Domain Layer ```typescript // domain/value-objects/CriticalError.ts export class CriticalError { private constructor( readonly code: string, readonly message: string, readonly severity: 'blocking' | 'warning', readonly category: 'dose' | 'protocol' | 'content' | 'patient' ) {} static createBlockingDoseError( code: string, message: string ): CriticalError { return new CriticalError(code, message, 'blocking', 'dose'); } static createBlockingProtocolError( code: string, message: string ): CriticalError { return new CriticalError(code, message, 'blocking', 'protocol'); } static createWarning( code: string, message: string, category: CriticalError['category'] ): CriticalError { return new CriticalError(code, message, 'warning', category); } isBlocking(): boolean { return this.severity === 'blocking'; } } // domain/services/CriticalErrorDetector.ts export class CriticalErrorDetector { detectDoseErrors( dose: number, doseRange: DoseRange, drug: Drug, route: AdministrationRoute ): CriticalError[] { const errors: CriticalError[] = []; // Dosis fuera de rango if (!doseRange.isValid(dose)) { if (dose < doseRange.min) { errors.push(CriticalError.createBlockingDoseError( 'DOSE_BELOW_MINIMUM', `Dosis ${dose}${doseRange.unit} está por debajo del mínimo terapéutico (${doseRange.min}${doseRange.unit})` )); } else { errors.push(CriticalError.createBlockingDoseError( 'DOSE_ABOVE_MAXIMUM', `Dosis ${dose}${doseRange.unit} excede el máximo seguro (${doseRange.max}${doseRange.unit}). RIESGO DE MUERTE.` )); } } // Dosis letal (10x el máximo) if (dose > doseRange.max * 10) { errors.push(CriticalError.createBlockingDoseError( 'DOSE_LETHAL', `Dosis ${dose}${doseRange.unit} es LETAL. Máximo seguro: ${doseRange.max}${doseRange.unit}. BLOQUEAR ADMINISTRACIÓN.` )); } // Vía incorrecta if (!drug.routes.includes(route)) { errors.push(CriticalError.createBlockingDoseError( 'INVALID_ROUTE', `Vía ${route} no está indicada para ${drug.genericName}. Vías permitidas: ${drug.routes.join(', ')}` )); } // Advertencias const warningLevel = doseRange.getWarningLevel(dose); if (warningLevel === 'high') { errors.push(CriticalError.createWarning( 'DOSE_HIGH_LIMIT', `Dosis está en el límite superior del rango. Monitorizar efectos adversos.`, 'dose' )); } return errors; } detectProtocolErrors( protocol: Protocol, executedSteps: number[] ): CriticalError[] { const errors: CriticalError[] = []; // Pasos críticos omitidos const criticalSteps = protocol.steps .filter(s => s.type === 'critical') .map(s => s.order); const missingCritical = criticalSteps.filter( step => !executedSteps.includes(step) ); if (missingCritical.length > 0) { errors.push(CriticalError.createBlockingProtocolError( 'CRITICAL_STEPS_MISSING', `Pasos críticos no ejecutados: ${missingCritical.join(', ')}. No se puede continuar el protocolo.` )); } // Orden incorrecto const sortedExecuted = [...executedSteps].sort((a, b) => a - b); if (JSON.stringify(executedSteps) !== JSON.stringify(sortedExecuted)) { errors.push(CriticalError.createBlockingProtocolError( 'STEPS_OUT_OF_ORDER', `Los pasos se ejecutaron fuera de orden. Orden correcto: ${sortedExecuted.join(' → ')}` )); } return errors; } detectPatientErrors( weight: PatientWeight, age: PatientAge, protocol: Protocol ): CriticalError[] { const errors: CriticalError[] = []; // Peso inválido para edad const weightValidation = weight.isValidForAge(age); if (!weightValidation.valid) { errors.push(CriticalError.createBlockingDoseError( 'INVALID_WEIGHT_FOR_AGE', weightValidation.warning || 'Peso inválido para la edad del paciente' )); } // Protocolo incompatible con edad if (protocol.ageGroup !== 'todos' && protocol.ageGroup !== age.getAgeGroup()) { errors.push(CriticalError.createBlockingProtocolError( 'AGE_INCOMPATIBLE', `Protocolo "${protocol.title}" no es aplicable a grupo de edad ${age.getAgeGroup()}. Protocolo diseñado para: ${protocol.ageGroup}` )); } return errors; } detectContentErrors( content: ContentItem, reviews: MedicalReview[] ): CriticalError[] { const errors: CriticalError[] = []; // Contenido no validado if (content.status !== 'approved' && content.status !== 'published') { errors.push(CriticalError.createBlockingProtocolError( 'CONTENT_NOT_VALIDATED', `Contenido "${content.title}" no ha sido validado médicamente. Estado actual: ${content.status}` )); } // Contenido rechazado con errores críticos const rejectedReviews = reviews.filter(r => r.status === 'rejected'); const criticalRejections = rejectedReviews.filter(review => review.comments.some(c => c.severity === 'critical') ); if (criticalRejections.length > 0) { errors.push(CriticalError.createBlockingProtocolError( 'CONTENT_REJECTED_CRITICAL', `Contenido rechazado por errores críticos. Debe corregirse antes de publicar.` )); } return errors; } } // domain/services/ActionBlocker.ts export class ActionBlocker { constructor(private readonly errorDetector: CriticalErrorDetector) {} async canExecuteAction( action: 'administer_drug' | 'execute_protocol' | 'publish_content', context: ActionContext ): Promise<{ allowed: boolean; blockingErrors: CriticalError[]; warnings: CriticalError[]; }> { const allErrors: CriticalError[] = []; switch (action) { case 'administer_drug': if (context.dose && context.doseRange && context.drug && context.route) { const doseErrors = this.errorDetector.detectDoseErrors( context.dose, context.doseRange, context.drug, context.route ); allErrors.push(...doseErrors); } if (context.weight && context.age) { const patientErrors = this.errorDetector.detectPatientErrors( context.weight, context.age, context.protocol! ); allErrors.push(...patientErrors); } break; case 'execute_protocol': if (context.protocol && context.executedSteps) { const protocolErrors = this.errorDetector.detectProtocolErrors( context.protocol, context.executedSteps ); allErrors.push(...protocolErrors); } break; case 'publish_content': if (context.content && context.reviews) { const contentErrors = this.errorDetector.detectContentErrors( context.content, context.reviews ); allErrors.push(...contentErrors); } break; } const blockingErrors = allErrors.filter(e => e.isBlocking()); const warnings = allErrors.filter(e => !e.isBlocking()); return { allowed: blockingErrors.length === 0, blockingErrors, warnings }; } } export interface ActionContext { dose?: number; doseRange?: DoseRange; drug?: Drug; route?: AdministrationRoute; weight?: PatientWeight; age?: PatientAge; protocol?: Protocol; executedSteps?: number[]; content?: ContentItem; reviews?: MedicalReview[]; } ``` --- ## ✅ Casos de Uso ### Caso 1: Bloquear administración de dosis letal ```typescript const blocker = new ActionBlocker(errorDetector); const result = await blocker.canExecuteAction('administer_drug', { dose: 10, // mg - letal doseRange: DoseRange.create(0.5, 1, 'mg', 'adulto'), drug: adrenalina, route: 'IV', weight: PatientWeight.fromKg(70), age: PatientAge.fromYears(35) }); if (!result.allowed) { console.error('BLOQUEADO:', result.blockingErrors.map(e => e.message)); // Mostrar alerta crítica al usuario // Bloquear botón de administración } ``` ### Caso 2: Bloquear protocolo sin pasos críticos ```typescript const result = await blocker.canExecuteAction('execute_protocol', { protocol: rcpProtocol, executedSteps: [1, 2, 3] // Faltan pasos críticos 4 y 5 }); if (!result.allowed) { // Bloquear continuación del protocolo // Mostrar qué pasos críticos faltan } ``` --- ## 📊 Logging de Errores Críticos Todos los errores críticos deben registrarse: ```typescript interface CriticalErrorLog { errorCode: string; errorMessage: string; severity: 'blocking' | 'warning'; category: string; context: { userId?: string; contentId?: string; drugId?: string; protocolId?: string; action: string; }; timestamp: Date; blocked: boolean; } ``` --- **Fin del documento**