CreaRack-SL

Barra de Integridad F3: Veredicto "Cambio Real" abre incidencia CNS/ITSM

Barra de Integridad F3: Veredicto “Cambio Real” abre incidencia CNS/ITSM

Versión: 1.61.0 (2026-07-17)
Ámbito: Racks · Monitoring (CNS/ITSM)
Impacto usuario: Ninguno fuera de org 1 (módulo solo activo en org 1, calibración #202)

Resumen ejecutivo

Cuando un usuario de la Barra de Integridad (radar de sincronización planos ↔ red) emite un veredicto “Cambio real” en la cola de reconciliación, el sistema ahora abre automáticamente una incidencia AIInsight en el CNS/ITSM existente con:

  • Explicación generada por la IA (evidencia fresca del sensor como contexto).
  • Sin comandos ejecutables (la resolución es corregir el plano, no el dispositivo).
  • Caducidad de 14 días (no las 4h por defecto de una anomalía operativa).

Al reconocer (acknowledge) la incidencia en Observatory, un hook des-silencia el IntegrityAck vinculado → el próximo probe re-verifica el sujeto contra el plano:

  • Si el plano quedó corregido → pasa a matched_alive (aviso desaparece).
  • Si no → re-avisa en la cola.

Límite honesto v1: Solo hay incidencia si el sujeto tiene target de monitorización (FK obligatoria en AIInsight que CNS derreferencia por todas partes). Si no lo tiene, la API responde {queued: false, reason: "subject has no monitoring target"} y la página lo muestra.

Arquitectura y flujo

1. Veredicto “Real change” en el endpoint /api/racks/integrity/ack

POST /api/racks/integrity/ack
{
  "kind": "matched_stale" | "undocumented",
  "verdict": "real_change",
  "device_id" | "profile_id": <id>,
}

↓ (200 OK)
{
  "ok": true,
  "ack_id": <id>,
  "muted_until": "ISO8601",
  "gate": {...},
  "itsm": {
    "queued": true | false,
    "reason"?: "subject has no monitoring target"
  }
}

Nota: El campo itsm es None para otros veredictos (false_alarm, plan_updated).

2. Puente: racks/services/integrity_itsm.py

Responsabilidades:

  • resolve_ticket_target(org, device=None, profile=None): Devuelve el MonitoringTarget que ancla la incidencia, o None si no existe.

    • Para un device: busca target directo o por IP de gestión.
    • Para un profile: busca target por IP de la red.
    • Usa select_related("organization") para evitar SynchronousOnlyOperation en contexto async.
  • _drift_context(org, ack): Recomputa la evidencia fresca del sujeto desde el motor (fuente única de verdad).

    • Devuelve (reason, evidence, label) o (None, None, None) si el sujeto ya no aparece en compute_integrity().
    • Los tickets se abren siempre con subject_label preservado en el ack (inmutable desde el click).
  • open_drift_ticket(ack_id): Crea la incidencia AIInsight de documentación.

    • Idempotente: si el ack ya tiene insight_id, devuelve ese id sin crear otro.
    • Condicional: si no hay target, devuelve None sin crear (la API lo reporta en respuesta).
    • Async-safe: usa async_to_sync para llamar a create_drift_insight (que es async).

3. Tarea Huey: racks/tasks.open_integrity_drift_ticket(ack_id)

@db_task()
def open_integrity_drift_ticket(ack_id):
    # Best-effort: si falla (rate limit, proveedor caído), el aviso sigue en cola.
    integrity_itsm.open_drift_ticket(ack_id)

Rationale: La llamada al proveedor de IA y recomputo de contexto pueden tomar segundos. No caben en el request del click del usuario (que debe ser <100ms). Por eso se encola en background con Huey — el campo itsm en la respuesta dice {"queued": true} siempre que el target exista.

4. Servicio: monitoring/services/insight_service.create_drift_insight()

Nueva función especializada para tickets documentales (drift):

async def create_drift_insight(
    *,
    target: MonitoringTarget,
    anomaly_description: str,
    evidence: dict,
    subject_label: str,
    provider_name: str = "static",
) -> AIInsight:
    """Ticket ITSM de drift documental sin comandos ni maintenance-window."""

Diferencias vs. create_insight() operativo:

Aspectocreate_insight()create_drift_insight()
Ventana mantenimiento✅ Sí (anomalía operativa)❌ No (no afecta al equipo)
Comandos✅ Propuestos por IA❌ Siempre [] (nunca Apply)
is_actionable✅ Si hay comandos❌ Siempre False
TTL (expires_at)4 horas (por defecto)14 días (DRIFT_TICKET_TTL_DAYS)
RiskLevelHIGH/MEDIUM según IALOW (siempre)
CategoríaCONNECTIVITY/PERFORMANCE/etcOTHER

Reutilización sin duplicación: Se extrajo _analyze_with_fallback() de create_insight() para que ambas usen la misma lógica de IA + fallback a reglas estáticas.

5. Hook en acknowledge_insight() (monitoring/services/insight_service.py)

# Al reconocer una incidencia (usuario en Observatory):
def acknowledge_insight(insight: AIInsight, user=None, notes: str = ""):
    insight.status = AIInsight.Status.ACKNOWLEDGED
    insight.acknowledged_at = timezone.now()
    insight.acknowledged_by = user
    insight.save()
    
    # Barra de Integridad F3 — des-silenciar acks vinculados
    unmuted = IntegrityAck.objects.filter(
        insight=insight, 
        muted_until__gt=now
    ).update(muted_until=now)

Efecto: El próximo probe de integridad re-verifica el sujeto sin esperar a la caducidad original del silenciamiento (típicamente 30 días).

6. Nuevas migraciones

Migración racks/migrations/0018_integrityack_insight.py:

AddField(
    model_name="integrityack",
    name="insight",
    field=models.ForeignKey(
        "monitoring.AIInsight",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="integrity_acks",
    ),
)

FK nullable → permite acks sin incidencia (p.ej. sujeto sin target).

Internacionalización (i18n)

Completada: 28 strings JS + 11 de plantilla al catálogo (djangojs.po/django.po).

  • Página completa de Integrity ya traducida al español.
  • Novos strings de F3: “Real change recorded — an ITSM incident is being opened…” y variante sin target.
  • Chequeo de duplicados msgctxt-aware + check_po.py 0 errores.

Testing

8 tests nuevos en tests/racks/test_integrity_itsm.py (proveedor IA: “static” offline):

  • ✅ Ticket sin comandos y con TTL largo (14 días).
  • ✅ Idempotencia per ack_id.
  • ✅ Sin target → sin ticket + respuesta honesta.
  • ✅ Perfil undocumented anclado por IP.
  • ✅ Ciclo acknowledge → unmute → re-verify.
  • ✅ Endpoint /api/racks/integrity/ack con itsm field.
  • ✅ Veredictos otros (no real_change) no tocan ITSM.

139 tests de regresión CNS verdes en Docker.

Observaciones y deuda

  • Hueco pre-existente (no tocado): AIInsight.resolved_at no lo escribe ningún flujo (se consulta para métricas SLA de MTTR pero nunca se actualiza). Anotado para decisión aparte.
  • Limitación honesta v1: Solo hay ticket si target existe. Los avisos de integrity en sujetos sin monitorización siguen viviendo en la cola del radar (no se generan incidencias “huérfanas”).

Véase también

  • [[entity—racks—service—integrity-itsm]]
  • [[entity—monitoring—service—create-drift-insight]]
  • [[entity—racks—endpoint—integrity-ack]]
  • [[entity—racks—model—integrity-ack]]
  • [[entity—monitoring—model—aiinsight]]