# ¿Qué latencia debe tener una pasarela de ejecución de escritorio?

Una pasarela de ejecución de escritorio solo merece un sitio en el flujo si su demora de máquina queda por debajo del ruido del trabajo interactivo y el tiempo de aprobación se publica aparte. No aprobaría un despliegue basándome en una media, una sola ejecución caliente ni un gráfico que mezcla el tiempo que tarda alguien en tocar Touch ID con el de ejecución del software. Esos números pueden quedar bonitos, pero no dicen si la pasarela interrumpirá el ciclo de editar y probar.

Para HTTP normal y SSH ya conectado, uso como punto de partida un aumento máximo de 25 ms en p50 y 75 ms en p95 en los Mac de desarrollo previstos. Una conexión SSH nueva necesita un presupuesto distinto: 50 ms en p50 y 150 ms en p95. Son requisitos iniciales, no constantes universales. Cada equipo debe ajustarlos según su línea directa, frecuencia de llamadas, red y pruebas de percepción. El método siguiente aporta la evidencia necesaria sin esconder un extremo lento detrás de una mediana aceptable.

## Los percentiles deben describir la misma operación

p50 y p95 solo significan algo si cada muestra representa el mismo límite. p50 es la mediana: la mitad de las llamadas terminó en ese valor o antes. p95 es el valor que alcanzó como máximo el 95%. En 200 muestras ordenadas, la definición de rango más cercano toma la observación 190. Las diez más lentas siguen visibles en vez de desaparecer en una media.

Define el límite desde que el llamador inicia la operación hasta que recibe el resultado completo. Esa es la espera del agente. Incluye trabajo del proceso local, transporte, ejecución remota, transferencia de respuesta y todo el trabajo de la pasarela en la ruta. Excluye la preparación de la prueba y, en la pista desatendida, la confirmación humana.

Es habitual comparar límites distintos. El gráfico HTTP directo puede usar `time_starttransfer` de curl mientras el gráfico de la pasarela mide tiempo de pared alrededor de un proceso. El primero termina con el primer byte; el segundo espera a serializar y devolver todo el resultado. La pasarela pierde antes de empezar. Usa finalización total en ambas rutas y conserva las fases como columnas de diagnóstico.

La misma regla vale para SSH. Una conexión TCP nueva, el protocolo SSH, la autenticación, el comando y la desconexión forman una operación. Un comando por una conexión compartida ya abierta es otra. Informa de ellas por separado. Mezclarlas hace que p50 describa principalmente reutilización y p95, creación de conexión.

Publica al menos estos datos por clase:

- p50 y p95 de pared para la ruta directa
- p50 y p95 de pared para la ruta intermediada
- p50 y p95 del aumento calculado por pares
- número de muestras, fallos y tiempos agotados
- equipo, red, versión de pasarela y modo de aprobación

La distribución de diferencias emparejadas importa más que restar dos percentiles de portada. Para la pareja `i`, calcula `delta_i = brokered_i - direct_i` bajo la misma condición y después sus p50 y p95. `p95(brokered) - p95(direct)` aporta contexto, pero no es el p95 del coste y puede ocultar la relación entre picos de red y trabajo de la pasarela.

## Un contrato previo evita resultados convenientes

Escribe el contrato antes de medir, porque casi toda optimización accidental cambia la pregunta. Registra modelo de Mac, macOS, alimentación, estado térmico, ruta de red, región del servidor, protocolo HTTP, negociación SSH, tamaños, tiempo límite, concurrencia y reutilización. La carga de fondo debe parecerse a un equipo de desarrollo. Un portátil limpio con editor y compilación cerrados responde a una pregunta de laboratorio.

Usa un destino local y otro remoto realista. El local descubre el coste de máquina porque apenas hay variación de red. El remoto muestra si ese coste sigue importando junto al transporte real. Añadir 40 ms a una llamada local de 3 ms no se siente igual que añadirlos a una llamada remota de 300 ms, y ambos resultados deben aparecer.

Crea clases en vez de una suite mezclada. Como mínimo: respuesta HTTP pequeña con conexión reutilizada, la misma con conexión nueva, conexión SSH nueva que ejecute `true` y comando SSH sobre conexión disponible. Añade una carga y una respuesta representativas del trabajo del equipo. No uses una API destructiva ni un servidor SSH de producción.

Haz un calentamiento que no entre en la muestra. Luego recoge al menos 200 llamadas por clase alternando el orden directo e intermediado. Si ejecutas primero todas las directas, un cambio térmico, de red, DNS o caché posterior acabará contado como coste de la pasarela. El orden aleatorio sirve, pero la alternancia estricta es más fácil de revisar.

Usa concurrencia uno para la prueba interactiva. Un agente que espera una herramienta actúa en serie. Si varios agentes compartirán la pasarela, añade una prueba de carga etiquetada como capacidad. Veinte llamadas simultáneas descubren colas, pero no sustituyen la latencia de una llamada.

Los fallos se quedan en el informe. Registra el tiempo agotado como fallo con su duración y publica la tasa junto a los percentiles. Eliminar errores premia a una ruta que falla deprisa o pierde las llamadas lentas. Si tantos fallos vuelven inestable p95, repara primero la fiabilidad.

## Mide HTTP sin alterar su significado

HTTP directo e intermediado deben usar método, destino, cabeceras, cuerpo, límite y consumo de respuesta iguales. La fuente de la credencial puede cambiar porque aislarla es la función de la pasarela, pero el servidor debe recibir peticiones equivalentes. Antes de medir, confirma estado, cabeceras elegidas y hash del cuerpo.

Las variables write-out documentadas por curl dan relojes de fase. `time_connect` termina al conectar la red, `time_appconnect` incluye TLS, `time_starttransfer` llega al primer byte y `time_total` a la finalización. Sirven para localizar regresiones, pero el límite comparable sigue siendo el reloj del llamador porque el intermediario puede trabajar antes o después del cliente ascendente.

Esta sonda directa imprime cuatro campos en segundos sin salida de progreso:

```sh
curl -sS -o /dev/null -w '%{time_connect} %{time_appconnect} %{time_starttransfer} %{time_total}
' -H "$AUTH_HEADER" "$ENDPOINT"
```

Para medir pares, coloca la orden directa completa en `DIRECT_CMD` y la orden de la pasarela en `BROKER_CMD`. Este ejecutor alterna el orden, usa reloj monotónico, conserva fallos y emite un objeto JSON por observación. Trata cada orden como vector de argumentos para impedir que entren operadores o expansiones del shell.

```python
import json
import os
import shlex
import subprocess
import time

samples = int(os.environ.get("SAMPLES", "200"))
timeout = float(os.environ.get("TIMEOUT", "10"))
commands = {
    "direct": shlex.split(os.environ["DIRECT_CMD"]),
    "brokered": shlex.split(os.environ["BROKER_CMD"]),
}

for n in range(10):
    label = "direct" if n % 2 == 0 else "brokered"
    subprocess.run(commands[label], stdout=subprocess.DEVNULL,
                   stderr=subprocess.DEVNULL, timeout=timeout)

for n in range(samples):
    order = ("direct", "brokered") if n % 2 == 0 else ("brokered", "direct")
    for label in order:
        started = time.monotonic_ns()
        try:
            result = subprocess.run(commands[label], capture_output=True,
                                    timeout=timeout)
            ok = result.returncode == 0
            code = result.returncode
        except subprocess.TimeoutExpired:
            ok = False
            code = None
        elapsed_ms = (time.monotonic_ns() - started) / 1_000_000
        print(json.dumps({"pair": n, "path": label, "ms": elapsed_ms,
                          "ok": ok, "code": code}, separators=(",", ":")))
```

Guarda la salida como JSON Lines. Antes del cálculo, compara códigos de éxito y hashes fuera del bucle medido. Una petición intermediada que devuelve menos datos parecerá más rápida; una directa que sigue redirecciones cuando la otra no lo hace mide otro trabajo.

Controla también la reutilización. Si el protocolo y la pasarela conservan una conexión HTTP ascendente, compáralos con un cliente directo persistente. Lanzar curl de nuevo en cada muestra puede perder la reutilización y favorecer al intermediario. Fuerza conexiones nuevas en ambos lados o crea dos clientes persistentes, y publica la elección.

## SSH necesita mediciones nuevas y reutilizadas

La preparación SSH suele dominar un comando corto, por lo que un único percentil explica poco. El manual de OpenSSH documenta la conexión compartida con `ControlMaster` y `ControlPersist`. Las sesiones posteriores reutilizan transporte y autenticación. Es una optimización válida si ambas rutas reciben la misma oportunidad.

Para la clase nueva, desactiva la conexión compartida, ejecuta un comando inocuo como `true` y desconecta. En un servidor dedicado, la orden directa puede ser:

```sh
ssh -F /dev/null -o BatchMode=yes -o ControlMaster=no -o ConnectTimeout=10 "$TEST_HOST" true
```

La pareja intermediada debe usar la acción SSH normal con el mismo servidor, usuario y comando. No impongas una preparación artificial que el agente real nunca utilizará. Haz coincidir la ruta directa con el comportamiento documentado o declara la diferencia arquitectónica.

Para la clase reutilizada, abre la conexión antes del calentamiento y ejecuta solo `true` durante el muestreo. Verifica la reutilización. El diagnóstico detallado de OpenSSH muestra el contacto con el socket de control; la pasarela debe aportar tiempos o registros equivalentes. Si no puedes probarlo, etiqueta el estado como desconocido y no lo compares con uno conocido.

Repite después con una tarea real no destructiva, como leer un archivo pequeño fijo o consultar el estado de un servicio de prueba. `true` aísla conexión y ejecución, pero no cubre transferencia, decodificación ni límites. Fija el contenido y calcula el hash recibido.

La autenticación también cambia la comparación. No entregues una clave privada al agente solo para facilitar la línea directa. Ejecuta esa ruta en un arnés aislado con acceso equivalente y considera la exposición de la clave una condición de prueba, no un diseño recomendado. La latencia mide el coste de mediación; no decide si un proceso autónomo debe tener la clave.

## Instrumenta el trabajo interno de la pasarela

El cronómetro externo demuestra el resultado visible; las marcas internas explican la causa. Añade reloj monotónico al recibir, terminar validación, terminar autorización, completar la operación de credencial, enviar al destino, recibir el primer byte, terminar la respuesta, confirmar auditoría y entregar el resultado. Registra duraciones o un identificador opaco, nunca secretos ni cuerpos.

Estas marcas dividen el aumento en partes sobre las que actuar:

- análisis y serialización del protocolo local
- consulta de autorización y espera en cola
- operación de bóveda o credencial
- preparación del cliente y obtención de conexión
- persistencia de auditoría y devolución

No compares relojes de pared entre procesos sin resolver sus diferencias. En un Mac, el reloj monotónico de cada proceso sirve para duraciones, pero sus orígenes pueden no coincidir. Lo más seguro es que cada componente informe de su duración y unir los registros con un identificador de prueba.

La auditoría queda dentro del límite si la pasarela promete registrar antes de responder bien. Mover la escritura después gana la prueba debilitando la garantía. Agrupar escrituras puede ser válido, pero el punto de durabilidad publicado debe coincidir con producción. Mide el producto normal con diario y controles activos.

Perfila solo cuando las fases señalen al sospechoso. Si p50 crece con la respuesta, revisa copias y serialización. Si p95 sube y p50 no, revisa bloqueos, colas, vaciados de auditoría, fallos de caché de conexiones y planificación del sistema. El perfil CPU de una mediana tranquila rara vez explica un atasco extremo.

Protege la prueba del coste de observarla. El registro detallado en terminal puede bloquear E/S. Escribe eventos compactos a archivo o memoria, mide una vez con diagnóstico y comprueba que la diferencia sea despreciable. Mantén modos equivalentes en ambas rutas cuando sea posible.

## La confirmación humana tiene otro nivel de servicio

El tiempo de confirmación empieza cuando aparece la tarjeta y termina cuando la decisión llega a la llamada bloqueada. No es latencia de máquina. Mezclarlo con llamadas automáticas hace que p95 dependa de la mano, la atención, la pantalla y la interrupción. Eso mide si la aprobación encaja en el trabajo, no la eficiencia de ejecución.

Publica tres distribuciones: máquina antes del aviso, aviso visible hasta decisión y máquina desde la decisión hasta el resultado. El total puede aparecer al lado, siempre que nadie lo use para diagnosticar transporte.

No automatices clics y lo llames demora humana. La automatización mide presentación y transmisión de la decisión, ambas de máquina. Un estudio humano requiere participantes informados, una tarea definida y marcas que no capturen datos sensibles. Indica cuántos participaron y si esperaban el aviso. Un Touch ID esperado en un guion será más rápido que una aprobación inesperada al programar.

Los modos de aprobación también van en filas distintas. Una acción en sesión autorizada, la primera llamada que autoriza un proceso y una confirmación por uso siguen rutas diferentes. Mezclarlas deja el resultado a merced de su frecuencia accidental.

Un presupuesto útil describe la interrupción. Por ejemplo, exige que la tarjeta aparezca en 150 ms desde la llegada a p95, y mide la decisión humana sin aprobarla o suspenderla hasta observar uso real. Después fija objetivos según abandonos, errores y ruptura de la tarea. Una tarjeta rápida es mala si omite identidad o acción necesarias para decidir.

## Calcula las diferencias antes de dibujar

El calculador debe emparejar observaciones, sacar los pares incompletos de la distribución y contarlos como fallos. Así una llamada directa correcta no se compara con la siguiente intermediada después de que su pareja agotara tiempo. Usa el número de pareja como unión.

Emplea una sola definición. El rango más cercano ordena y elige `ceil(p * n)`, empezando en uno. Algunas bibliotecas interpolan y devuelven un valor no observado. La interpolación puede servir, pero cambiar de método entre cuaderno y panel crea discusiones cerca del límite. Decláralo.

Este calculador lee el JSON Lines anterior y muestra recuentos, p50 y p95 de rango cercano para tiempo directo, intermediado y aumento:

```python
import json
import math
import sys

rows = [json.loads(line) for line in sys.stdin if line.strip()]
by_pair = {}
failures = {"direct": 0, "brokered": 0}

for row in rows:
    if not row["ok"]:
        failures[row["path"]] += 1
        continue
    by_pair.setdefault(row["pair"], {})[row["path"]] = row["ms"]

direct = []
brokered = []
deltas = []
for pair in sorted(by_pair):
    values = by_pair[pair]
    if "direct" not in values or "brokered" not in values:
        continue
    direct.append(values["direct"])
    brokered.append(values["brokered"])
    deltas.append(values["brokered"] - values["direct"])

def percentile(values, fraction):
    ordered = sorted(values)
    rank = max(1, math.ceil(fraction * len(ordered)))
    return ordered[rank - 1]

result = {"complete_pairs": len(deltas), "failures": failures}
for name, values in (("direct", direct), ("brokered", brokered),
                     ("added", deltas)):
    result[name] = {
        "p50_ms": percentile(values, 0.50),
        "p95_ms": percentile(values, 0.95),
        "max_ms": max(values),
    }
print(json.dumps(result, separators=(",", ":")))
```

La salida contiene `complete_pairs`, fallos por ruta y bloques con `p50_ms`, `p95_ms` y `max_ms`. Añade intervalos de confianza si ya se usan, pero revisa también las filas lentas. Un percentil cambia por variación del sistema o del muestreo; los datos originales permiten distinguirlo.

Las diferencias negativas son válidas. Aparecen si el intermediario reutiliza una conexión que la orden directa abre o si el ruido favorece una muestra. No las conviertas en cero. Corrige una configuración injusta o conserva la variación. Recortarlas sesga el resultado al alza.

Conserva toda la precisión y redondea solo al presentar, por ejemplo a una décima de milisegundo. Evalúa el límite con el dato sin redondear. Un 75,0 mostrado puede estar apenas por encima del requisito.

## La cola lenta indica dónde se pierde la confianza

El trabajo interactivo castiga más una pausa irregular que un coste pequeño y estable. Un desarrollador se adapta a 15 ms en cada llamada. Una pausa aleatoria de 800 ms hace parecer roto al agente, sobre todo en una secuencia. Por eso p95 es un requisito.

Examina las observaciones lentas y etiqueta conexión HTTP nueva, protocolo SSH, desbloqueo, escritura de auditoría, despertar, presión de CPU o pico de red. No borres un valor porque puedas explicarlo. Si ocurrirá en producción, pertenece a la distribución. Retira solo un fallo probado de la prueba, conserva el original y publica la regla.

p99 ayuda durante ingeniería, pero 200 llamadas lo dejan en las dos más lentas. Exijo p50 y p95 para adoptar y reviso máximo y filas extremas. Una ejecución nocturna más larga puede sostener p99 después de superar la prueba interactiva.

Separa estados fríos y calientes. Mide inicio de app o desbloqueo como operaciones propias si el usuario las encuentra, y haz lo mismo con reposo y reactivación del Mac. Una app permanente puede ir bien tras calentarse y fallar en la primera acción después de comer.

Cuenta también el coste de secuencia. Doce llamadas en serie acumulan aumentos. Reproducir una secuencia capturada y limpiada descubre rotación de conexiones o un diario que se ralentiza. Conserva distribuciones individuales y añade la diferencia total como dato secundario.

## Fija el límite según el coste interactivo

La pasarela pasa cuando el aumento es pequeño tanto en términos absolutos como frente a la ruta directa. Empiezo con estos límites:

- HTTP reutilizado: 25 ms en p50 y 75 ms en p95
- HTTP nuevo: 30 ms en p50 y 100 ms en p95
- SSH reutilizado: 25 ms en p50 y 75 ms en p95
- SSH nuevo: 50 ms en p50 y 150 ms en p95

Aplica además un máximo relativo del 20% cuando el p95 directo alcance 100 ms. En una llamada local de 3 ms, el 20% exige menos de un milisegundo y mide ruido. En una llamada remota lenta, solo el límite absoluto puede permitir una fracción excesiva. Ambas reglas cubren casos distintos.

Exige una tasa de fallos no peor que la directa más una tolerancia pequeña declarada, e investiga cualquier tiempo agotado causado por la pasarela. No cambiaría fiabilidad por ganar 10 ms de mediana. Exige equivalencia del resultado y durabilidad prevista; una ruta que modifica la respuesta o registra tarde suspende otra prueba más seria.

Ejecuta en el Mac compatible más lento que use el equipo, no solo en el último modelo. Repite con alimentación y con editor más compilación. Para destinos remotos, haz tres ejecuciones en días o periodos distintos. Así una ventana de red favorable no se convierte en promesa.

Ajusta los números con una prueba ciega. Inyecta demoras fijas en una pasarela simulada, reproduce una tarea típica y pide valorar la interrupción sin revelar el retraso. Pon p95 por debajo de la primera demora que provoque quejas constantes. Conserva 150 ms para SSH nuevo solo si el protocolo directo domina y la tarea completa aún parece inmediata.

Rechaza el despliegue si una clase común incumple p95 aunque el agregado pase. Muchas llamadas HTTP rápidas pueden tapar SSH lento. Aprueba por clase y luego revisa la secuencia completa para comprobar que varios costes aceptables no sumen una pausa inaceptable.

## Haz que la adopción sea reversible y comprobable

Empieza con un grupo piloto y una condición de retirada escrita. Congela script, datos de prueba, configuración y calculador en control de versiones. Guarda observaciones, no capturas de pantalla. Un revisor debe poder recalcular y ver cada fallo.

Para Sallyport, ejecuta el mismo arnés por sus canales HTTP y SSH con bóveda, autorización de sesión, ajuste por llamada y auditoría iguales al despliegue previsto. Su registro cifrado y encadenado puede verificarse sin conexión mediante `sp audit verify`; ejecútalo después para que el informe y la comprobación cubran las mismas llamadas.

Amplía solo cuando cada clase pase en Mac representativos, coincidan los hashes, se verifique la auditoría y los desarrolladores acepten la reproducción completa. Mantén una salida directa en el piloto y registra por qué se usa. Si la gente evita la pasarela por un p95 irregular, la prueba omitió el flujo real. Corrige la medición antes de ampliar.
