# 07a. SENTINEL — Sistema de Auditoría **Manual de Arquitectura Técnica — Documento 07a** **Versión:** 1.2 **Dependencia:** `S-CONTRACT.md` **Estado:** Enterprise Standard --- # A.1 Introducción a SENTINEL SENTINEL es el **sistema de auditoría automatizada** del ecosistema GRACE. Opera como un guardián silencioso que verifica la integridad, calidad y conformidad de todas las operaciones. ``` ┌─────────────────────────────────────────────────────────────────┐ │ PRINCIPIO SENTINEL │ │ │ │ "Confía, pero verifica. Verifica todo, siempre, sin excepción"│ └─────────────────────────────────────────────────────────────────┘ ``` ## A.1.1 Objetivos 1. **Integridad**: Verificar que los datos no han sido alterados. 2. **Conformidad**: Validar cumplimiento del contrato común. 3. **Calidad**: Detectar degradación en los módulos IA. 4. **Anomalías**: Identificar patrones sospechosos o errores sistemáticos. 5. **Trazabilidad**: Asegurar que todo evento tiene su registro. ## A.1.2 Filosofía Dual SENTINEL implementa una estrategia de **dos velocidades**: | Aspecto | SENTINEL-LIGHT | SENTINEL-DEEP | |---------|----------------|---------------| | Analogía | Guardia de seguridad | Detective investigador | | Enfoque | Exhaustivo, superficial | Selectivo, profundo | | Motor | Reglas + ML ligero | LLM pesado | | Frecuencia | Continua (cada 5 min) | Periódica (cada hora) | | Costo | Mínimo | Variable | --- # A.2 SENTINEL-LIGHT (Centinela Rápido) ## A.2.1 Arquitectura ``` ┌─────────────────────────────────────────────────────────────────┐ │ SENTINEL-LIGHT │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ RULES │ │ STATS │ │ ALERTS │ │ │ │ ENGINE │───▶│ COLLECTOR │───▶│ DISPATCHER │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ ┌─────────────────────────────────────────────────────────────┤ │ │ SYS_LOG (Source) │ │ └─────────────────────────────────────────────────────────────┤ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────┤ │ │ AUDIT_RESULTS (Sink) │ │ └─────────────────────────────────────────────────────────────┤ │ │ └─────────────────────────────────────────────────────────────────┘ ``` ## A.2.2 Ciclo de Ejecución ``` CADA 5 MINUTOS: ┌────────────────────────────────────────────────────────────────┐ │ 1. QUERY │ │ SELECT * FROM SYS_LOG │ │ WHERE audit_status = 'PENDING' │ │ AND timestamp_created > NOW() - INTERVAL '10 minutes' │ │ ORDER BY timestamp_created │ │ LIMIT 1000 │ └────────────────────────────────────────────────────────────────┘ │ ▼ ┌────────────────────────────────────────────────────────────────┐ │ 2. APPLY RULES │ │ Para cada registro: │ │ - Ejecutar reglas de integridad │ │ - Ejecutar reglas de conformidad │ │ - Ejecutar reglas de rendimiento │ │ - Acumular resultados │ └────────────────────────────────────────────────────────────────┘ │ ▼ ┌────────────────────────────────────────────────────────────────┐ │ 3. CLASSIFY │ │ - PASS: Todas las reglas OK │ │ - WARN: Reglas de severidad MEDIUM fallaron │ │ - FAIL: Reglas de severidad HIGH/CRITICAL fallaron │ └────────────────────────────────────────────────────────────────┘ │ ▼ ┌────────────────────────────────────────────────────────────────┐ │ 4. UPDATE & ESCALATE │ │ - UPDATE SYS_LOG SET audit_status, audit_light_ts │ │ - INSERT AUDIT_RESULTS con resumen del batch │ │ - Si FAIL → marcar para SENTINEL-DEEP │ │ - Si CRITICAL → alerta inmediata │ └────────────────────────────────────────────────────────────────┘ ``` ## A.2.3 Catálogo de Reglas ### Reglas de Integridad (I-*) ```yaml rules: I-001: name: "Hash de entrada coincide" description: "Verificar que input_hash == SHA256(archivo en input_ref)" severity: CRITICAL condition: | VERIFY_HASH(input_ref, input_hash) == true on_fail: action: ESCALATE_DEEP alert: IMMEDIATE I-002: name: "Hash de salida coincide" description: "Verificar que output_hash == SHA256(archivo en output_ref)" severity: CRITICAL condition: | output_ref IS NULL OR VERIFY_HASH(output_ref, output_hash) == true on_fail: action: ESCALATE_DEEP alert: IMMEDIATE I-003: name: "Cadena de trazas válida" description: "Si tiene parent_trace_id, el padre debe existir" severity: HIGH condition: | parent_trace_id IS NULL OR EXISTS(SELECT 1 FROM SYS_LOG WHERE trace_id = parent_trace_id) on_fail: action: LOG_WARN alert: BATCH I-004: name: "Referencias no huérfanas" description: "input_ref y output_ref deben apuntar a archivos existentes" severity: HIGH condition: | FILE_EXISTS(input_ref) == true AND (output_ref IS NULL OR FILE_EXISTS(output_ref) == true) on_fail: action: ESCALATE_DEEP alert: BATCH I-005: name: "Idempotency key única por trace" description: "No debe haber duplicados de idempotency_key con distinto resultado" severity: MEDIUM condition: | NOT EXISTS( SELECT 1 FROM SYS_LOG WHERE idempotency_key = {current.idempotency_key} AND trace_id != {current.trace_id} AND output_hash != {current.output_hash} ) on_fail: action: LOG_WARN alert: BATCH ``` ### Reglas de Conformidad (C-*) ```yaml rules: C-001: name: "Campos obligatorios presentes" description: "Verificar campos requeridos según perfil" severity: HIGH condition: | IF profile == 'FULL': ALL_PRESENT(trace_id, step_id, idempotency_key, module_name, input_hash, input_ref, status_code) ELSE: ALL_PRESENT(trace_id, idempotency_key, module_name, status_code) on_fail: action: LOG_ERROR alert: BATCH C-002: name: "Status code válido" description: "status_code debe ser un valor permitido" severity: HIGH condition: | status_code IN ('PENDING', 'RUNNING', 'SUCCESS', 'PARTIAL', 'ERROR', 'TIMEOUT', 'FALLBACK', 'CANCELLED') on_fail: action: LOG_ERROR alert: BATCH C-003: name: "Step type válido" description: "step_type debe ser un valor permitido" severity: HIGH condition: | step_type IN ('INIT', 'VALIDATE', 'ROUTE', 'TRANSFORM', 'FALLBACK', 'PERSIST', 'NOTIFY', 'COMPLETE', 'ERROR', 'AUDIT', 'CANCEL') on_fail: action: LOG_ERROR alert: BATCH C-004: name: "Confidence en rango" description: "Si existe confidence, debe estar entre 0 y 1" severity: MEDIUM condition: | confidence IS NULL OR (confidence >= 0 AND confidence <= 1) on_fail: action: LOG_WARN alert: BATCH C-005: name: "Timestamps coherentes" description: "timestamp_completed >= timestamp_started >= timestamp_created" severity: MEDIUM condition: | timestamp_started IS NULL OR timestamp_started >= timestamp_created AND timestamp_completed IS NULL OR timestamp_completed >= timestamp_started on_fail: action: LOG_WARN alert: BATCH C-006: name: "Batch coherente" description: "Si is_batch, debe tener batch_id y item_index válidos" severity: MEDIUM condition: | batch_id IS NULL OR (batch_item_index > 0 AND batch_items_total >= batch_item_index) on_fail: action: LOG_WARN alert: BATCH ``` ### Reglas de Rendimiento (P-*) ```yaml rules: P-001: name: "Duración dentro de umbral" description: "duration_ms no debe exceder 3x el esperado para el módulo" severity: MEDIUM params: thresholds: CLASSIFIER: 1500 OCR_CORE: 10000 ASR_ENGINE: 60000 SUMMARIZER: 5000 EMBEDDINGS: 2000 default: 30000 condition: | duration_ms IS NULL OR duration_ms < GET_THRESHOLD(module_name) * 3 on_fail: action: LOG_WARN alert: BATCH P-002: name: "Tasa de error por módulo" description: "Tasa de errores en última hora < 5%" severity: HIGH scope: AGGREGATE window: 1 HOUR condition: | (SELECT COUNT(*) FILTER(WHERE status_code IN ('ERROR', 'TIMEOUT'))::float / NULLIF(COUNT(*), 0) FROM SYS_LOG WHERE module_name = {current.module_name} AND timestamp_created > NOW() - INTERVAL '1 hour' ) < 0.05 on_fail: action: ESCALATE_DEEP alert: IMMEDIATE P-003: name: "Fallback excesivo" description: "Tasa de fallback en última hora < 20%" severity: MEDIUM scope: AGGREGATE window: 1 HOUR condition: | (SELECT COUNT(*) FILTER(WHERE status_code = 'FALLBACK')::float / NULLIF(COUNT(*), 0) FROM SYS_LOG WHERE module_name = {current.module_name} AND timestamp_created > NOW() - INTERVAL '1 hour' ) < 0.20 on_fail: action: LOG_WARN alert: BATCH P-004: name: "Confidence por debajo de baseline" description: "Confidence promedio no debe caer más de 0.1 del baseline" severity: MEDIUM scope: AGGREGATE window: 1 HOUR params: baselines: CLASSIFIER: 0.90 OCR_CORE: 0.85 ASR_ENGINE: 0.80 default: 0.80 condition: | (SELECT AVG(confidence) FROM SYS_LOG WHERE module_name = {current.module_name} AND confidence IS NOT NULL AND timestamp_created > NOW() - INTERVAL '1 hour' ) >= GET_BASELINE(module_name) - 0.10 on_fail: action: ESCALATE_DEEP alert: BATCH ``` ### Reglas de Seguridad (S-*) ```yaml rules: S-001: name: "PII con cifrado adecuado" description: "Si pii_detected=true, encryption_profile debe ser E2E_BASIC o superior" severity: HIGH condition: | pii_detected == false OR encryption_profile IN ('E2E_BASIC', 'E2E_STRICT') on_fail: action: ESCALATE_DEEP alert: IMMEDIATE S-002: name: "Datos SECRET con cifrado estricto" description: "Si data_sensitivity=SECRET, encryption_profile debe ser E2E_STRICT" severity: CRITICAL condition: | data_sensitivity != 'SECRET' OR encryption_profile == 'E2E_STRICT' on_fail: action: ESCALATE_DEEP alert: IMMEDIATE S-003: name: "Key Vault referenciado para cifrado" description: "Si encryption_profile != NONE, debe existir key_vault_ref" severity: HIGH condition: | encryption_profile == 'NONE' OR key_vault_ref IS NOT NULL on_fail: action: LOG_ERROR alert: BATCH ``` ## A.2.4 Output de SENTINEL-LIGHT ```json { "sentinel_type": "LIGHT", "version": "1.2", "execution_id": "uuid-ejecucion", "timestamp_start": "2025-12-01T10:30:00.000Z", "timestamp_end": "2025-12-01T10:30:03.450Z", "duration_ms": 3450, "scope": { "window_start": "2025-12-01T10:20:00.000Z", "window_end": "2025-12-01T10:30:00.000Z", "records_queried": 1250, "records_processed": 1250 }, "summary": { "passed": 1230, "warnings": 15, "failures": 5, "pass_rate": 0.984 }, "by_severity": { "CRITICAL": 1, "HIGH": 3, "MEDIUM": 16, "LOW": 0 }, "by_rule": { "I-001": {"passed": 1250, "failed": 0}, "I-002": {"passed": 1249, "failed": 1}, "C-001": {"passed": 1248, "failed": 2}, "P-001": {"passed": 1235, "failed": 15}, "S-001": {"passed": 1248, "failed": 2} }, "by_module": { "CLASSIFIER": {"total": 500, "passed": 498, "failed": 2}, "OCR_CORE": {"total": 300, "passed": 295, "failed": 5}, "ASR_ENGINE": {"total": 50, "passed": 48, "failed": 2} }, "escalations": [ { "trace_id": "uuid-problema-1", "step_id": "uuid-step-1", "rule_violated": "I-002", "severity": "CRITICAL", "details": "Output hash mismatch: expected abc123, got def456", "escalate_to": "DEEP", "alert_sent": true }, { "trace_id": "uuid-problema-2", "step_id": "uuid-step-2", "rule_violated": "S-001", "severity": "HIGH", "details": "PII detected but encryption_profile=NONE", "escalate_to": "DEEP", "alert_sent": true } ], "aggregate_alerts": [ { "rule": "P-002", "module": "OCR_CORE", "message": "Error rate 6.2% exceeds threshold 5%", "severity": "HIGH", "escalate_to": "DEEP" } ], "next_run": "2025-12-01T10:35:00.000Z" } ``` --- # A.3 SENTINEL-DEEP (Analista Profundo) ## A.3.1 Arquitectura ``` ┌─────────────────────────────────────────────────────────────────┐ │ SENTINEL-DEEP │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ QUEUE │ │ LLM │ │ REPORT │ │ │ │ MANAGER │───▶│ ANALYZER │───▶│ GENERATOR │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │ │ │ │ │ │ │ │ │ │ ┌──────┴──────┐ ┌──────┴──────┐ ┌──────┴──────┐ │ │ │ Escalations │ │ Claude/ │ │ Findings │ │ │ │ + Sampling │ │ GPT-4o │ │ + Actions │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` ## A.3.2 Triggers de Ejecución | Trigger | Condición | Prioridad | Acción | |---------|-----------|-----------|--------| | `ESCALATION` | SENTINEL-LIGHT marcó para DEEP | ALTA | Analizar inmediatamente | | `ERROR_CRITICAL` | Regla CRITICAL violada | CRÍTICA | Analizar + alertar | | `HOURLY_SAMPLE` | Cron cada hora | NORMAL | Muestreo aleatorio | | `PATTERN_DETECTED` | Mismo error 3+ veces | ALTA | Investigar patrón | | `DRIFT_ALERT` | Confidence < baseline - 0.1 | MEDIA | Analizar tendencia | | `MANUAL` | Solicitud de operador | VARIABLE | Según solicitud | ## A.3.3 Estrategia de Muestreo ```python def select_for_deep_analysis(window_hours=1): """ Selecciona registros para análisis profundo. """ candidates = [] # 1. TODOS los escalados de LIGHT (prioridad máxima) escalated = query(""" SELECT * FROM SYS_LOG WHERE audit_status IN ('LIGHT_WARN', 'LIGHT_FAIL') AND audit_deep_ts IS NULL AND timestamp_created > NOW() - INTERVAL '{window_hours} hours' """) candidates.extend(escalated) # 2. TODOS los errores no analizados errors = query(""" SELECT * FROM SYS_LOG WHERE status_code IN ('ERROR', 'TIMEOUT') AND audit_deep_ts IS NULL AND timestamp_created > NOW() - INTERVAL '{window_hours} hours' """) candidates.extend(errors) # 3. Muestreo aleatorio de exitosos (5% o mínimo 10) success_count = query(""" SELECT COUNT(*) FROM SYS_LOG WHERE status_code = 'SUCCESS' AND audit_deep_ts IS NULL AND timestamp_created > NOW() - INTERVAL '{window_hours} hours' """) sample_size = max(10, int(success_count * 0.05)) sampled = query(f""" SELECT * FROM SYS_LOG WHERE status_code = 'SUCCESS' AND audit_deep_ts IS NULL AND timestamp_created > NOW() - INTERVAL '{window_hours} hours' ORDER BY RANDOM() LIMIT {sample_size} """) candidates.extend(sampled) # 4. Registros con confidence anormalmente baja low_confidence = query(""" SELECT * FROM SYS_LOG WHERE confidence IS NOT NULL AND confidence < 0.6 AND audit_deep_ts IS NULL AND timestamp_created > NOW() - INTERVAL '{window_hours} hours' """) candidates.extend(low_confidence) return deduplicate(candidates) ``` ## A.3.4 Prompt del Sistema SENTINEL-DEEP ```markdown # ROL Eres SENTINEL-DEEP, el sistema de auditoría profunda del ecosistema GRACE. Tu función es analizar registros del sistema para: 1. Verificar cumplimiento del contrato común 2. Investigar causa raíz de errores 3. Detectar patrones y anomalías 4. Validar calidad y coherencia de resultados # CONTEXTO DEL SISTEMA - **Arquitectura**: GRACE (18 microservicios cognitivos) + ALFRED (orquestador n8n) - **Filosofía**: "Alfred Decide, GRACE Transforma" - **Contrato**: S-CONTRACT v1.2 - **Perfiles**: FULL (trazabilidad completa), LITE (mínimo necesario) # CONOCIMIENTO DEL CONTRATO ## Campos obligatorios FULL - envelope: trace_id, step_id, idempotency_key, timestamp_init - routing: module - payload: type, content - storage: input_ref, input_hash ## Campos obligatorios LITE - envelope: trace_id, idempotency_key - routing: module - payload: type, content ## Status codes válidos SUCCESS, PARTIAL, ERROR, TIMEOUT, FALLBACK, PENDING, RUNNING, CANCELLED ## Step types válidos INIT, VALIDATE, ROUTE, TRANSFORM, FALLBACK, PERSIST, NOTIFY, COMPLETE, ERROR, AUDIT, CANCEL # TAREA Analiza los registros proporcionados y genera un informe estructurado. Para cada registro, evalúa: ## 1. CONFORMIDAD DEL CONTRATO - ¿Tiene todos los campos obligatorios según su perfil? - ¿Los valores están en rangos válidos? - ¿La estructura es coherente internamente? ## 2. INTEGRIDAD DE DATOS - ¿Los hashes son consistentes? - ¿Las referencias apuntan a recursos válidos? - ¿La cadena de trazas es válida? ## 3. CALIDAD DEL RESULTADO - ¿El confidence reportado es creíble? - ¿El resultado tiene sentido para el tipo de input? - ¿Hay señales de alucinación o error del modelo? ## 4. CAUSA RAÍZ (si hay error) - ¿Qué falló exactamente? - ¿Es problema de datos, módulo, infraestructura, o configuración? - ¿Es caso aislado o patrón recurrente? ## 5. RECOMENDACIÓN - Acción correctiva inmediata - Prevención para el futuro - ¿Requiere escalado a humano? # OUTPUT OBLIGATORIO Responde ÚNICAMENTE con un JSON válido siguiendo este schema exacto: ```json { "analysis_id": "uuid", "timestamp": "ISO8601", "records_analyzed": 0, "findings": [ { "trace_id": "uuid", "step_id": "uuid", "diagnosis": { "contract_compliant": true, "integrity_valid": true, "quality_credible": true, "issues": [] }, "root_cause": { "category": "DATA|MODULE|INFRA|CONFIG|UNKNOWN|NONE", "description": "string", "is_pattern": false, "pattern_id": null, "related_traces": [] }, "recommendation": { "action": "string", "prevention": "string", "urgency": "LOW|MEDIUM|HIGH|CRITICAL", "escalate_human": false, "escalate_reason": null }, "confidence": 0.95 } ], "patterns_detected": [], "summary": { "total_issues": 0, "critical": 0, "high": 0, "medium": 0, "low": 0, "human_escalation_needed": false } } ``` # REGLAS CRÍTICAS 1. NUNCA inventes datos que no estén en los registros 2. Si no puedes determinar algo, usa "UNKNOWN" y confidence bajo 3. Sé conservador con las recomendaciones de escalado a humano 4. Busca PATRONES entre múltiples registros 5. El output debe ser JSON válido, sin texto adicional ``` ## A.3.5 Flujo de Análisis ``` ┌────────────────────────────────────────────────────────────────┐ │ 1. COLLECT │ │ - Recoger registros según estrategia de muestreo │ │ - Agrupar por trace_id para contexto completo │ │ - Cargar datos relacionados (input/output refs) │ └────────────────────────────────────────────────────────────────┘ │ ▼ ┌────────────────────────────────────────────────────────────────┐ │ 2. ENRICH │ │ - Añadir contexto del M-CONTRACT del módulo │ │ - Añadir historial reciente del mismo módulo │ │ - Añadir métricas de baseline │ └────────────────────────────────────────────────────────────────┘ │ ▼ ┌────────────────────────────────────────────────────────────────┐ │ 3. ANALYZE (LLM) │ │ - Enviar al LLM con prompt del sistema │ │ - Timeout: 60 segundos │ │ - Fallback: GPT-4o → Claude → Local-Summary │ └────────────────────────────────────────────────────────────────┘ │ ▼ ┌────────────────────────────────────────────────────────────────┐ │ 4. VALIDATE │ │ - Parsear JSON de respuesta │ │ - Validar schema │ │ - Verificar coherencia de findings │ └────────────────────────────────────────────────────────────────┘ │ ▼ ┌────────────────────────────────────────────────────────────────┐ │ 5. PERSIST │ │ - INSERT en AUDIT_RESULTS │ │ - UPDATE SYS_LOG.audit_status, audit_deep_ts │ │ - Registrar patrones detectados │ └────────────────────────────────────────────────────────────────┘ │ ▼ ┌────────────────────────────────────────────────────────────────┐ │ 6. ESCALATE │ │ - Si human_escalation_needed → notificar │ │ - Si pattern detectado → crear alerta │ │ - Si acción urgente → trigger workflow │ └────────────────────────────────────────────────────────────────┘ ``` ## A.3.6 Output de SENTINEL-DEEP ```json { "sentinel_type": "DEEP", "version": "1.2", "analysis_id": "uuid-analysis", "trigger": "ESCALATION", "timestamp_start": "2025-12-01T11:00:00.000Z", "timestamp_end": "2025-12-01T11:00:12.350Z", "duration_ms": 12350, "model_used": { "provider": "openrouter", "model_id": "anthropic/claude-sonnet-4-20250514", "tokens_input": 4500, "tokens_output": 1200, "cost_units": 0.0089 }, "scope": { "records_queried": 25, "records_analyzed": 25, "trace_ids": ["uuid-1", "uuid-2", "..."] }, "findings": [ { "trace_id": "uuid-problema-1", "step_id": "uuid-step-1", "diagnosis": { "contract_compliant": false, "integrity_valid": true, "quality_credible": true, "issues": [ "Campo 'coverage' ausente en response de OCR_CORE", "Schema output_v1 requiere coverage cuando confidence < 0.8" ] }, "root_cause": { "category": "MODULE", "description": "OCR_CORE v1.0 no incluye campo coverage cuando la imagen tiene baja calidad. El módulo debería retornar coverage=0 en estos casos.", "is_pattern": true, "pattern_id": "PAT-2025-001", "related_traces": ["uuid-2", "uuid-5", "uuid-12"] }, "recommendation": { "action": "Actualizar OCR_CORE para incluir coverage=0.0 cuando confidence < 0.5", "prevention": "Añadir validación de schema en Alfred antes de persistir", "urgency": "MEDIUM", "escalate_human": true, "escalate_reason": "Requiere cambio de código en módulo" }, "confidence": 0.92 } ], "patterns_detected": [ { "pattern_id": "PAT-2025-001", "description": "OCR_CORE omite campo coverage en respuestas de baja confianza", "occurrences": 4, "first_seen": "2025-12-01T08:15:00.000Z", "affected_module": "OCR_CORE", "severity": "MEDIUM", "recommended_fix": "Patch en M-CONTRACT de OCR_CORE" } ], "contract_audit": { "total_checked": 25, "compliant": 21, "non_compliant": 4, "compliance_rate": 0.84, "common_violations": [ {"field": "coverage", "count": 4}, {"field": "tokens_output", "count": 1} ] }, "summary": { "total_issues": 5, "critical": 0, "high": 1, "medium": 3, "low": 1, "human_escalation_needed": true, "patterns_found": 1, "new_patterns": 1 }, "actions_triggered": [ { "type": "NOTIFICATION", "target": "admin@tzzr.pro", "template": "sentinel_escalation", "sent_at": "2025-12-01T11:00:15.000Z" } ], "next_scheduled": "2025-12-01T12:00:00.000Z" } ``` --- # A.4 Tabla AUDIT_RESULTS ```sql CREATE TABLE AUDIT_RESULTS ( -- ═══════════════════════════════════════════════════════════ -- IDENTIFICACIÓN -- ═══════════════════════════════════════════════════════════ id UUID PRIMARY KEY DEFAULT gen_random_uuid(), sentinel_type VARCHAR(10) NOT NULL, execution_id UUID NOT NULL, timestamp TIMESTAMPTZ DEFAULT NOW(), -- ═══════════════════════════════════════════════════════════ -- TRIGGER Y SCOPE -- ═══════════════════════════════════════════════════════════ trigger_type VARCHAR(50), window_start TIMESTAMPTZ, window_end TIMESTAMPTZ, records_queried INTEGER, records_processed INTEGER, -- ═══════════════════════════════════════════════════════════ -- EJECUCIÓN -- ═══════════════════════════════════════════════════════════ duration_ms INTEGER, model_used VARCHAR(100), tokens_input INTEGER, tokens_output INTEGER, cost_units DECIMAL(12,8), -- ═══════════════════════════════════════════════════════════ -- RESULTADOS AGREGADOS -- ═══════════════════════════════════════════════════════════ passed_count INTEGER, warning_count INTEGER, failure_count INTEGER, pass_rate DECIMAL(5,4), -- ═══════════════════════════════════════════════════════════ -- DETALLES (JSONB) -- ═══════════════════════════════════════════════════════════ findings JSONB, patterns_detected JSONB, by_severity JSONB, by_rule JSONB, by_module JSONB, escalations JSONB, -- ═══════════════════════════════════════════════════════════ -- ESCALADO -- ═══════════════════════════════════════════════════════════ human_escalation BOOLEAN DEFAULT FALSE, escalation_notified BOOLEAN DEFAULT FALSE, escalation_notified_at TIMESTAMPTZ, escalation_acknowledged BOOLEAN, escalation_acknowledged_by VARCHAR(100), escalation_resolved BOOLEAN, resolution_notes TEXT, resolution_timestamp TIMESTAMPTZ, -- ═══════════════════════════════════════════════════════════ -- REFERENCIAS -- ═══════════════════════════════════════════════════════════ related_traces UUID[], pattern_ids VARCHAR(50)[], -- ═══════════════════════════════════════════════════════════ -- CONSTRAINTS -- ═══════════════════════════════════════════════════════════ CONSTRAINT valid_sentinel_type CHECK ( sentinel_type IN ('LIGHT', 'DEEP') ) ); -- ═══════════════════════════════════════════════════════════════ -- ÍNDICES -- ═══════════════════════════════════════════════════════════════ CREATE INDEX idx_audit_timestamp ON AUDIT_RESULTS(timestamp); CREATE INDEX idx_audit_type ON AUDIT_RESULTS(sentinel_type); CREATE INDEX idx_audit_escalation ON AUDIT_RESULTS(human_escalation, escalation_resolved) WHERE human_escalation = TRUE; CREATE INDEX idx_audit_patterns ON AUDIT_RESULTS USING GIN(pattern_ids); ``` --- # A.5 Tabla PATTERNS (Detección de Patrones) ```sql CREATE TABLE AUDIT_PATTERNS ( -- ═══════════════════════════════════════════════════════════ -- IDENTIFICACIÓN -- ═══════════════════════════════════════════════════════════ id UUID PRIMARY KEY DEFAULT gen_random_uuid(), pattern_id VARCHAR(50) UNIQUE NOT NULL, -- ═══════════════════════════════════════════════════════════ -- DESCRIPCIÓN -- ═══════════════════════════════════════════════════════════ description TEXT NOT NULL, category VARCHAR(50), affected_module VARCHAR(50), severity VARCHAR(20), -- ═══════════════════════════════════════════════════════════ -- ESTADÍSTICAS -- ═══════════════════════════════════════════════════════════ first_seen TIMESTAMPTZ NOT NULL, last_seen TIMESTAMPTZ NOT NULL, occurrence_count INTEGER DEFAULT 1, affected_traces UUID[], -- ═══════════════════════════════════════════════════════════ -- RESOLUCIÓN -- ═══════════════════════════════════════════════════════════ status VARCHAR(20) DEFAULT 'OPEN', recommended_fix TEXT, fix_implemented BOOLEAN DEFAULT FALSE, fix_implemented_at TIMESTAMPTZ, fix_notes TEXT, -- ═══════════════════════════════════════════════════════════ -- METADATA -- ═══════════════════════════════════════════════════════════ created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW(), created_by VARCHAR(50) DEFAULT 'SENTINEL-DEEP', -- ═══════════════════════════════════════════════════════════ -- CONSTRAINTS -- ═══════════════════════════════════════════════════════════ CONSTRAINT valid_status CHECK ( status IN ('OPEN', 'INVESTIGATING', 'FIXING', 'RESOLVED', 'WONT_FIX', 'DUPLICATE') ), CONSTRAINT valid_severity CHECK ( severity IN ('LOW', 'MEDIUM', 'HIGH', 'CRITICAL') ) ); -- Índices CREATE INDEX idx_patterns_status ON AUDIT_PATTERNS(status) WHERE status != 'RESOLVED'; CREATE INDEX idx_patterns_module ON AUDIT_PATTERNS(affected_module); CREATE INDEX idx_patterns_severity ON AUDIT_PATTERNS(severity); ``` --- # A.6 Integración con Alfred (n8n) ## A.6.1 Workflow SENTINEL-LIGHT ``` ┌─────────────────────────────────────────────────────────────────┐ │ WORKFLOW: SENTINEL-LIGHT │ │ Trigger: Schedule (*/5 * * * *) │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ [Schedule Trigger] │ │ │ │ │ ▼ │ │ [NocoDB: Query SYS_LOG] │ │ "audit_status = PENDING AND created > -10min" │ │ │ │ │ ▼ │ │ [Function: Apply Rules] │ │ - Integrity rules │ │ - Conformity rules │ │ - Performance rules │ │ - Security rules │ │ │ │ │ ├─────────────┬─────────────┐ │ │ ▼ ▼ ▼ │ │ [If: PASS] [If: WARN] [If: FAIL] │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ [NocoDB: [NocoDB: [NocoDB: │ │ Update Update Update │ │ LIGHT_PASS] LIGHT_WARN] LIGHT_FAIL] │ │ │ │ │ │ │ │ │ ▼ │ │ │ │ [Queue for DEEP] │ │ │ │ │ │ │ └─────────────┴─────────────┘ │ │ │ │ │ ▼ │ │ [NocoDB: Insert AUDIT_RESULTS] │ │ │ │ │ ▼ │ │ [If: Critical Alert] │ │ │ │ │ ▼ │ │ [Send Notification] │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` ## A.6.2 Workflow SENTINEL-DEEP ``` ┌─────────────────────────────────────────────────────────────────┐ │ WORKFLOW: SENTINEL-DEEP │ │ Trigger: Schedule (0 * * * *) + Webhook │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ [Schedule/Webhook Trigger] │ │ │ │ │ ▼ │ │ [Function: Select Candidates] │ │ - Escalations from LIGHT │ │ - Errors not analyzed │ │ - Random sampling │ │ - Low confidence records │ │ │ │ │ ▼ │ │ [NocoDB: Fetch Full Records + Context] │ │ │ │ │ ▼ │ │ [Function: Enrich with M-CONTRACT] │ │ │ │ │ ▼ │ │ [OpenRouter: Claude/GPT-4] │ │ - System prompt: SENTINEL-DEEP │ │ - User prompt: Records JSON │ │ - Response: Analysis JSON │ │ │ │ │ ▼ │ │ [Function: Validate Response] │ │ │ │ │ ├─────────────┬─────────────┐ │ │ ▼ ▼ ▼ │ │ [NocoDB: [NocoDB: [NocoDB: │ │ Update Insert Upsert │ │ SYS_LOG] AUDIT_ PATTERNS] │ │ RESULTS] │ │ │ │ │ │ │ └─────────────┴─────────────┘ │ │ │ │ │ ▼ │ │ [If: Human Escalation] │ │ │ │ │ ▼ │ │ [Send Detailed Notification] │ │ - Email with findings │ │ - Link to dashboard │ │ - Recommended actions │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` --- # A.7 Métricas y Dashboard ## A.7.1 KPIs de SENTINEL | Métrica | Fórmula | Target | Alerta | |---------|---------|--------|--------| | Pass Rate (LIGHT) | passed / total | > 98% | < 95% | | Error Rate | errors / total | < 2% | > 5% | | Escalation Rate | escalated / total | < 5% | > 10% | | MTTR (Mean Time To Resolve) | avg(resolution_time) | < 4h | > 8h | | Pattern Detection Rate | patterns_new / week | informativo | > 5/semana | | False Positive Rate | false_positives / escalations | < 10% | > 20% | ## A.7.2 Queries para Dashboard ```sql -- Pass rate últimas 24h SELECT DATE_TRUNC('hour', timestamp) as hour, passed_count::float / NULLIF(records_processed, 0) as pass_rate FROM AUDIT_RESULTS WHERE sentinel_type = 'LIGHT' AND timestamp > NOW() - INTERVAL '24 hours' ORDER BY hour; -- Top 5 reglas más violadas SELECT rule_key, SUM((rule_data->>'failed')::int) as total_failures FROM AUDIT_RESULTS, jsonb_each(by_rule) as rules(rule_key, rule_data) WHERE timestamp > NOW() - INTERVAL '7 days' GROUP BY rule_key ORDER BY total_failures DESC LIMIT 5; -- Patrones abiertos por severidad SELECT severity, COUNT(*) FROM AUDIT_PATTERNS WHERE status = 'OPEN' GROUP BY severity; -- Escalaciones pendientes SELECT * FROM AUDIT_RESULTS WHERE human_escalation = TRUE AND escalation_resolved = FALSE ORDER BY timestamp DESC; ``` --- # A.8 Checklist de Implementación SENTINEL ## SENTINEL-LIGHT: - [ ] Crear workflow en n8n con trigger cada 5 min - [ ] Implementar todas las reglas I-*, C-*, P-*, S-* - [ ] Configurar umbrales por módulo - [ ] Crear notificaciones para alertas CRITICAL - [ ] Implementar actualización de SYS_LOG - [ ] Crear inserts en AUDIT_RESULTS ## SENTINEL-DEEP: - [ ] Crear workflow con trigger horario + webhook - [ ] Implementar estrategia de muestreo - [ ] Configurar integración con OpenRouter (Claude/GPT-4) - [ ] Implementar validación de respuesta JSON - [ ] Crear sistema de detección de patrones - [ ] Implementar notificaciones con findings detallados ## Tablas: - [ ] Crear tabla AUDIT_RESULTS con índices - [ ] Crear tabla AUDIT_PATTERNS con índices - [ ] Configurar políticas de retención ## Monitoreo: - [ ] Dashboard con KPIs - [ ] Alertas por Slack/Email - [ ] Reportes semanales automáticos --- **Fin del Documento 07a — SENTINEL** *Referencia: `S-CONTRACT.md` v1.2*