codigo0/docs/ERRORES_CRITICOS_MEDICOS.md

430 lines
12 KiB
Markdown
Raw Normal View History

# ⚠️ 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**