Volver a la wiki

Barra de Integridad F2: el radar interno plano↔realidad

Descripción general

La Barra de Integridad F2 cierra la visión interna del motor de validación plano↔realidad (iniciado en Motor de Integridad F1, v1.59.0). Permite ver el estado completo del radar de cada organización y gestionar la cola de reconciliación mediante veredictos humanos.

Ubicación (desde v1.63.0, 2026-07-23): pestaña Integrity de Observatory (Overview · CNS · ITSM · Integrity). Hasta v1.62.x fue página propia (/integrity/) con botón en el header; esa URL se conserva como redirect a /monitoring/?tab=integrity (bookmarks). Razón del traslado (decisión de producto de Edu): el bucle drift → filtro CNS → ticket ITSM → reconciliar vive en superficies vecinas y la audiencia es la misma.

Publicación: v1.60.0 (2026-07-17)
Estado: Nace completamente apagado — no visible para usuario alguno hasta que se active el módulo integrity en extra_modules de una org específica (beta org 1, activación manual post-deploy).


Por qué “nace apagado”

La v1.59.0 introdujo el motor que detecta divergencias plano↔realidad:

Pero la calidad del motor depende de su precisión, que a su vez depende del ambiente de testing. La v1.60.0 añade los veredictos humanos (IntegrityAck) que calibran automáticamente la métrica del gate (#202):

Hasta que el ratio señal/ruido sea digno de confianza, no se muestra a clientes. Por eso:

# core/migrations/0030_integrity_module.py
# El módulo existe pero NO se añade a ningún Plan ni org
SaaSModule.objects.get_or_create(
    slug="integrity",
    defaults={...}
)

Solo con org.extra_modules.add(integrity_module) → aparece la pestaña Integrity en Observatory + API.


Arquitectura de componentes

1. Módulo SaaS integrity (core)

2. Modelo IntegrityAck (racks)

3. Servicio Motor (racks/services/integrity.py)

Funciones públicas:

Sub-componentes:

4. API (racks/api/integrity.py)

Dos endpoints públicos:

GET /api/racks/integrity/overview (usuario miembro org)

{
  "result": { "summary": {...}, "racks": [{...}], "undocumented": [...] },
  "history": [{"date": "2026-07-10", "fidelity_pct": 98.5, ...}, ...],
  "gate": { "signal": 12, "noise": 2, "signal_pct": 85.7, ... }
}

POST /api/racks/integrity/ack (usuario racks:edit)

{
  "kind": "matched_stale",
  "verdict": "plan_updated",
  "device_id": 42,
  "note": "El switch ya está retirado del rack"
}
→ 200 { "ok": true, "ack_id": 1234, "muted_until": "2026-07-20T...", "gate": {...} }

Gateado por módulo — 403 sin integrity activo.

5. Vistas (desde v1.63.0)

6. Template + CSS + JS


Flujo de uso (beta)

  1. Admin añade módulo a org:

    org = Organization.objects.get(pk=1)
    org.extra_modules.add(SaaSModule.objects.get(slug="integrity"))
  2. Usuario racks:edit abre Observatory → pestaña Integrity (el enlace antiguo /integrity/ redirige ahí).

  3. Cola de reconciliación: Cada hallazgo (device stale, undocumented, etc.) es un botón.

    • Click plan_updated → Nota “ya corregí el plan” + 3 días silencio.
    • Click real_change → Nota “el cambio era real” + 30 días silencio.
    • Click false_alarm → Equipo va a cuarentena “muted” + 30 días silencio. No castiga fidelity.
  4. Gate #202: Con cada click, gate_stats(org) se actualiza automáticamente:

    signal_pct = (plan_updated + real_change) / total * 100
  5. Decisión de Go/NoGo: Cuando signal_pct > 85% (ej.), la feature sale de beta → se activa en Plan 4 (Professional) o superior.


Cambios en el modelo de datos

IntegrityAck (NEW)

class IntegrityAck(models.Model):
    organization: FK(Organization)  # Tenant
    device: FK(Device, null=True)   # Para avisos de rack
    profile: FK(DeviceProfile, null=True)  # Para undocumented
    kind: str  # matched_stale | ip_conflict | undocumented
    verdict: str  # plan_updated | real_change | false_alarm
    note: TextField  # Justificación usuario
    subject_label: str  # Nombre legible (cacheado en caso de borrado)
    muted_until: DateTimeField  # Fin del período de silencio
    created_by: FK(User)
    created_at: DateTimeField(auto_now_add=True)
    
    indices: [organization, muted_until]

RackIntegritySnapshot (modified v1.59.0)

Se mantuvo igual. Esta tabla almacena fotos diarias de fidelity/coverage por rack. F2 la lee para mostrar histórico.


Integración con el motor (v1.59.0)

La función compute_integrity() ahora carga veredictos vigentes al principio:

acks = _load_active_acks(organization, now)  # Índice por (tipo_sujeto, id, kind)

Luego, para cada hallazgo:

ack = acks.get(("device", device.id, CLASS_MATCHED_STALE))
if ack:
    finding["ack"] = {...verdict, muted_until, note...}
    if ack.verdict == VERDICT_FALSE_ALARM:
        counts["muted"] += 1  # No castiga fidelity
    else:
        counts[CLASS_MATCHED_STALE] += 1  # Sigue siendo hallazgo

fidelity_pct = 100 * matched_alive / (total - unobservable - muted)

Efecto práctico: Un false_alarm sobre “Device A” stale → Device A se excluye de fidelity durante 30 días. El plano sigue siendo inexacto, pero no penaliza la métrica.


Migraciones

core/0030_integrity_module

Seeds el módulo SaaS integrity en la tabla core_saasmodule. No lo asigna a ningún Plan.

racks/0016_integrity_ack

Crea tabla racks_integrityack con fields y índice org+muted_until.

racks/0017_integrity_ack_rls

Habilita RLS + crea política tenant_isolation con bypass admin.


Permisos y gating


Testing

12 tests en tests/racks/test_integrity_api.py (10 de F2 + 2 de la reubicación v1.63.0):

Nota CI (v1.63.0): los tests que renderizan páginas con {% vite_asset %} exigieron DJANGO_VITE dev_mode=True en config/settings/test.py — los shards de backend del CI no compilan el frontend y el manifest no existe.


Véase también

Subir