# ¿Qué proceso real del agente está solicitando la aprobación?

Un prompt de aprobación que dice «Terminal» o «zsh» suele ser demasiado impreciso para permitir una decisión real. Esos programas pueden formar parte de la ruta de lanzamiento, pero a menudo son infraestructura compartida. El revisor necesita saber qué ejecución del agente solicitó la acción, qué la inició y si algún elemento de esa cadena le proporciona una identidad duradera.

La dificultad está en que un árbol de procesos responde a varias preguntas distintas a la vez. Puede decirte quién creó a quién. Puede revelar un ejecutor de tareas que nadie esperaba. Puede mostrar que un IDE gráfico inició la terminal. Por sí solo, no puede demostrar que un proceso padre firmado aprobó cada línea de código que un proceso hijo vaya a ejecutar. Un buen diseño de aprobaciones usa el árbol como prueba y después formula la afirmación concreta que esa prueba permite sostener.

## La aprobación corresponde a un actor, no a una terminal

El actor que aprueba el revisor es la ejecución del proceso que solicitó la acción, interpretada en el contexto de su cadena de lanzamiento. Una ventana de terminal es un lugar donde se ejecutan procesos. No es automáticamente el programa responsable.

La diferencia parece minuciosa hasta que un equipo tiene varios agentes funcionando desde un mismo shell. Un desarrollador abre una terminal en iTerm2, ejecuta un agente de programación interactivo en una pestaña, inicia una tarea del repositorio en otra y deja un observador en segundo plano en una tercera. Los tres procesos descendientes pueden tener la misma aplicación de terminal en algún punto superior de la cadena. Llamar «iTerm2» a cada solicitud no aporta casi nada al revisor.

El error contrario también es habitual. Un revisor ve `node`, `python` o `zsh` y supone que el binario más cercano a la solicitud constituye toda la identidad. Ese binario puede ser el intérprete de un paquete de agente, un enlace de un gestor de paquetes, un script temporal generado por una extensión del IDE o un envoltorio añadido por una herramienta del equipo. El nombre es técnicamente correcto, pero no sirve para operar.

Usa internamente estas tres etiquetas, aunque la tarjeta de aprobación solo muestre dos:

- **Solicitante**: el proceso que realmente pidió usar el canal protegido.
- **Cadena de ejecución**: los procesos padre relevantes que explican cómo llegó allí el solicitante.
- **Autoridad reconocible**: el antecesor más cercano cuya identidad el revisor pueda reconocer razonablemente y cuya firma de código se pueda evaluar.

Estas etiquetas resuelven un problema que muchos sistemas de aprobación confunden. El solicitante responde a «¿qué proceso hizo esta llamada?». La autoridad reconocible responde a «¿a través de qué persona u organización llegó esta ejecución?». Están relacionados, pero no son intercambiables.

Por ejemplo, una solicitud podría verse así:

```text
Code Helper (aplicación firmada)
  └─ zsh -l
      └─ npm run agent
          └─ node ./tools/start-agent.mjs
              └─ agent-cli --workspace /Users/maya/src/payments
```

Si `agent-cli` realiza una solicitud HTTP, es el solicitante. El shell y npm explican la ruta de lanzamiento. El proceso firmado Code Helper puede ser la mejor autoridad reconocible, siempre que esté realmente en la ascendencia activa y no se limite a estar abierto en el escritorio. Una tarjeta que diga «agent-cli, iniciado desde Code Helper» es honesta. Una que solo diga «Code Helper quiere acceder» elimina la información que distinguiría esta ejecución de otra extensión o tarea.

## Un árbol de procesos es una prueba, no una declaración de intención

Un proceso padre demuestra que creó una relación con un proceso hijo o que la heredó. No demuestra que el padre comprendiera los argumentos posteriores del hijo, su prompt, las instrucciones del repositorio o la respuesta remota.

Esta limitación importa cuando se habla de firmas sin precisión. Apple describe el requisito designado como el mecanismo que usa macOS para determinar si el código sigue siendo el mismo entre versiones. Por lo general incorpora un identificador y una autoridad de firma. Apple también deja claro el límite: el código sin firma no tiene un requisito designado duradero, y las firmas locales ad hoc no proporcionan una identidad estable entre versiones.

Esto resulta útil como prueba para una aprobación. No es una aprobación de la acción. Un IDE firmado puede iniciar un script sin firma del repositorio. Una terminal firmada puede ejecutar un binario descargado. Un ejecutor de tareas firmado puede pasar variables de entorno maliciosas a un intérprete perfectamente normal. La firma permite al revisor reconocer a un editor de código. No sanea la cadena que hay debajo.

Distingue estas afirmaciones:

| Afirmación | Qué la respalda | Qué no respalda |
|---|---|---|
| «Esta solicitud provino del PID 81234». | El registro de procesos local | Quién escribió el código o por qué hizo la llamada |
| «Este proceso hijo fue iniciado por este padre». | El PID del padre y la ascendencia activa | Que el padre aprobara el comportamiento actual del hijo |
| «Este ejecutable está firmado por esta autoridad». | La inspección de la firma y el requisito designado | Que el ejecutable sea benigno o actúe conforme a la intención |
| «Esta ejecución comenzó desde este IDE o terminal». | Una cadena de antecesores activa e ininterrumpida | Que la ventana visible iniciara a todos los descendientes |

La consecuencia es práctica. No reduzcas las cuatro afirmaciones a un icono amigable y una sola frase. El revisor debe poder ver qué afirmación está aceptando.

Cuando la cadena sea ambigua, dilo. «Iniciado por un script sin firma desde una sesión de terminal» es una etiqueta de aprobación mejor que atribuir la solicitud a la aplicación de terminal como si la terminal hubiera escrito el código. La falsa precisión enseña a la gente a ignorar la tarjeta.

## Inspecciona la cadena activa antes de diseñar la tarjeta de aprobación

La forma más rápida de encontrar al actor correcto es inspeccionar una ejecución real mientras siga activa. Hazlo con el agente realizando una operación inofensiva, porque la cadena puede cambiar cuando el trabajo pasa del arranque a un ejecutor de tareas o a un ayudante hijo.

En macOS, empieza por el PID del solicitante si tu puerta de enlace lo registra. Este comando muestra el proceso, su PID padre, la hora de inicio, el nombre del ejecutable y los argumentos:

```sh
ps -p "$PID" -o pid=,ppid=,lstart=,user=,comm=,args=
```

Un resultado representativo tiene esta forma:

```text
81234 80991 Tue Jul 21 14:08:31 2026 maya /usr/local/bin/agent-cli agent-cli --workspace /Users/maya/src/payments
```

Después, recorre la cadena hacia arriba. `ps` no lo hace de forma recursiva, así que una pequeña función de shell mantiene la inspección repetible:

```sh
ancestry() {
  local pid="$1"
  while [ "$pid" -gt 1 ] 2>/dev/null; do
    ps -p "$pid" -o pid=,ppid=,user=,comm=,args=
    pid=$(ps -p "$pid" -o ppid= | tr -d ' ')
  done
}

ancestry "$PID"
```

El resultado es deliberadamente sencillo. Busca cambios de tipo: de ejecutable del agente a intérprete, de intérprete a gestor de paquetes, de gestor de paquetes a shell, y de shell a terminal o ayudante del IDE. No te detengas al reconocer un nombre. Continúa hasta llegar a un proceso que proporcione al revisor una procedencia significativa o hasta que termine la cadena local.

Para obtener una vista general cuando existan varios descendientes, esta vista resulta útil:

```sh
ps -ax -o pid=,ppid=,user=,lstart=,comm=,args= | sort -n
```

Úsala para investigar, no para mostrarla en un prompt de aprobación. Un listado completo de procesos contiene demasiados detalles irrelevantes y muy poca estructura. Tu implementación debe recopilarlo cuando sea necesario y después reducirlo al solicitante, el padre inmediato, la autoridad reconocible y los saltos intermedios que expliquen un lanzamiento inesperado.

Aquí hay dos problemas de tiempo. Primero, los identificadores de proceso se pueden reutilizar después de que un proceso termine. Registra la hora de inicio junto con el PID y resuelve la cadena en el momento de la solicitud en lugar de confiar en una muestra anterior. Segundo, un ejecutor de tareas puede crear un trabajador y terminar antes de que se realice la llamada protegida. Si el padre ya no existe, conserva la cadena observada desde la creación del proceso o informa del enlace perdido. No lo sustituyas por un padre supuesto.

## VS Code cambia la ruta de lanzamiento sin convertirse en el solicitante

La terminal integrada de VS Code puede inyectar argumentos o variables de entorno al iniciar shells compatibles para activar la integración del shell. Su documentación también indica que la inyección automática no cubre todas las configuraciones, incluidos algunos subshells, SSH y casos de shells complejos. Es un buen recordatorio de que los metadatos de la interfaz de la terminal y la ascendencia Unix son fuentes de pruebas distintas.

En un caso sencillo, la cadena se ve así:

```text
Visual Studio Code
  └─ ayudante de terminal
      └─ shell de inicio de sesión
          └─ agent-cli
```

El ayudante de terminal existe porque el IDE necesita administrar un pseudoterminal y su shell hijo. El shell de inicio de sesión puede cargar un perfil que cambie `PATH`, active un entorno de lenguaje, instale hooks del shell o inicie un script específico del proyecto. Estos detalles explican por qué el mismo comando se comporta de forma distinta en una terminal independiente y en la terminal del IDE.

La decisión de aprobación no debe fingir que esos detalles no existen. Muestra el ejecutable del agente como solicitante. Presenta VS Code como autoridad de lanzamiento solo cuando forme parte de la ascendencia actual. Incluye el shell y el comando de tarea en una ruta desplegable cuando distingan de forma importante la ejecución.

Un error frecuente tiene este aspecto:

```text
Visual Studio Code
  └─ zsh
      └─ make test-agent
          └─ sh -c ./scripts/bootstrap-agent
              └─ python3 tools/agent.py
```

Una implementación ingenua ve `zsh` y etiqueta la solicitud como «terminal de VS Code». Otra ve `python3` y la etiqueta como «Python». Ninguna ayuda al revisor a determinar si se trata de la tarea conocida del repositorio o de un proceso cualquiera iniciado desde el mismo shell.

La afirmación útil se acerca más a esta:

```text
Solicitante: python3 tools/agent.py
Ruta de ejecución: make test-agent > scripts/bootstrap-agent
Iniciado desde: terminal integrada de Visual Studio Code
```

Esta etiqueta tiene un coste: deja claro que la solicitud provino de código controlado por el repositorio. Debe hacerlo. Si un revisor confía en un editor firmado, pero no en la copia de trabajo actual, la tarjeta debe dejar espacio para esa decisión.

No infieras que VS Code es el propietario a partir de secuencias de escape de la terminal, variables de entorno o un título de ventana. Pueden haberse heredado, copiado o quedado obsoletos. La ascendencia activa es una prueba más sólida. El contexto que proporciona el IDE puede enriquecer la pantalla después de establecer esa ascendencia, pero no sustituirla.

## Una terminal independiente aporta contexto, no autoridad absoluta

iTerm2, Terminal y aplicaciones parecidas ofrecen a una persona un punto de entrada deliberado. Si un desarrollador abre una terminal limpia, escribe un comando del agente y recibe inmediatamente un prompt de aprobación, identificar la aplicación de terminal es un contexto útil. Indica dónde comenzó la ejecución.

Aun así, no justifica aprobar automáticamente cada proceso hijo que comparta esa sesión. Los shells están diseñados para crear hijos, y las pestañas de larga duración suelen sobrevivir al motivo por el que se abrieron. Un comando iniciado a las 9:00 en una pestaña puede dejar un trabajador activo hasta la hora de comer, después de un `cd`, un cambio de rama, una modificación del entorno y tres comandos no relacionados.

Haz visible la diferencia:

```text
Solicitante: agent-cli --workspace /Users/maya/src/payments
Padre: zsh -l
Origen interactivo: aplicación de terminal firmada
Inicio de sesión: Tue Jul 21 14:08:31 2026
```

El origen interactivo es un contexto de apoyo. La sesión pertenece a la ejecución real del proceso. Si el proceso termina, revoca la aprobación de la sesión aunque la pestaña de terminal siga abierta. Un proceso nuevo del agente en la misma pestaña es una ejecución nueva, con argumentos nuevos y posiblemente otro ejecutable en `PATH`.

Aquí es donde la identidad reconocible para el revisor se vuelve incómoda. La aplicación de terminal puede estar firmada por un editor conocido, mientras que `agent-cli` puede ser un ejecutable sin firma instalado en un directorio del proyecto. La tarjeta honesta debe nombrar ambos hechos. No transfieras la firma de la terminal al hijo sin firma como si la terminal lo hubiera avalado.

El mismo problema aparece con las funciones y alias del shell. Un comando que parece `agent` puede expandirse a una función que cambia de directorio, carga un archivo de entorno, inicia un script de paquete y finalmente ejecuta otro binario. Tu recopilador de procesos solo verá los hijos resultantes. Si quieres mostrar el comando escrito como contexto opcional, captúralo mediante la integración del shell o un hook del shell y etiquétalo como historial de comandos introducidos por el usuario, no como identidad del proceso.

## Los scripts y ejecutores de tareas crean las cadenas más engañosas

Los ejecutores de tareas son populares porque convierten una configuración local compleja en un comando fácil de recordar. Esa comodidad suele hacer que sus cadenas de procesos parezcan más confiables de lo que son.

Considera este lanzamiento normal:

```text
zsh -l
  └─ just agent
      └─ /bin/sh -cu 'npm run agent -- --mode write'
          └─ npm run agent -- --mode write
              └─ sh -c node ./scripts/launch-agent.js --mode write
                  └─ node ./scripts/launch-agent.js --mode write
                      └─ agent-cli --mode write
```

Cada proceso intermedio tiene un propósito legítimo. `just` selecciona una receta. `/bin/sh` interpreta el cuerpo de la receta. npm selecciona un script de paquete e inicia un shell. Node evalúa el script de lanzamiento. Sin embargo, ninguno de esos nombres indica al revisor si la solicitud protegida provino de una ejecución conocida del agente o de otro comando que casualmente usó el mismo entorno de ejecución.

No elimines las capas intermedias del registro de auditoría. Son precisamente el lugar donde una investigación encontrará un script de paquete modificado, un hook de ciclo de vida `pre` o `post` inesperado, un `PATH` alterado o un script del repositorio que sustituyó otro ejecutable. Redúcelas en la pantalla de aprobación solo después de conservarlas en un registro de eventos inmutable.

Una regla de presentación razonable es:

1. Coloca primero el ejecutable y los argumentos del solicitante.
2. Muestra el iniciador directo cuando cambie la interpretación, como `npm run deploy-agent` o `make migration-bot`.
3. Nombra como autoridad de lanzamiento al primer antecesor firmado reconocible.
4. Mantén toda la ascendencia observada disponible en los detalles del evento.

Esta regla evita dos malos hábitos. El primero es ocultar todos los envoltorios, lo que hace invisible una tarea modificada. El segundo es mostrar un árbol de siete líneas antes de que el revisor pueda responder a la solicitud. Cuando las tarjetas de aprobación son ilegibles, la gente aprueba la forma de la tarjeta, no la acción.

Rechazo una recomendación: aprobar el ejecutor de tareas en lugar del agente porque el nombre de la tarea es más fácil de reconocer para una persona. Los nombres de las tareas suelen ser texto del repositorio. Cualquiera que pueda modificar la copia de trabajo quizá pueda cambiar lo que hace la tarea. Usa el nombre de la tarea como contexto, pero vincula la sesión de aprobación a la ejecución observada del proceso del agente y vuelve a evaluarla cuando comience una ejecución nueva.

## Elige con cuidado el límite de firma

El antecesor firmado más cercano suele ser la mejor autoridad reconocible, pero «más cercano» requiere criterio. Un shell o intérprete del sistema firmado puede estar justo encima de un script sin firma. Llamar autoridad a ese shell produce una etiqueta técnicamente firmada que no dice quién proporcionó el código.

La guía de firma de código de Apple distingue el identificador de un ejecutable de su identidad de firma y usa un requisito designado para establecer la identidad del código. La herramienta `codesign` puede mostrar ese requisito. Usa el resultado para identificar una aplicación o ayudante firmado, pero no trates una cadena de identificador por sí sola como prueba de una relación con el editor.

Para una aplicación incluida en un paquete, inspecciona el ejecutable o paquete de aplicación que aparezca en la ascendencia:

```sh
codesign --display --verbose=4 --requirements - "/Applications/Example.app" 2>&1 \
  | sed -n '/Identifier=/p;/TeamIdentifier=/p;/Authority=/p;/designated =>/p'
```

Un resultado típico contiene campos de esta forma:

```text
Identifier=com.example.editor
TeamIdentifier=AB12CDE345
Authority=Developer ID Application: Example Software (AB12CDE345)
designated => anchor apple generic and identifier "com.example.editor" and certificate leaf[subject.OU] = "AB12CDE345"
```

Los campos exactos varían según la firma y la ruta de distribución. Lo importante es que el sistema de aprobación capture una identidad de código estable cuando macOS pueda proporcionarla y después muestre una versión comprensible para el revisor sin ocultar las pruebas originales.

Aplica estas reglas al elegir la autoridad mostrada:

- Da preferencia a una aplicación gráfica firmada o a un ayudante firmado específico que esté realmente en la ascendencia del solicitante.
- No uses un intérprete genérico firmado como sustituto del script sin firma que ejecutó.
- No infieras autoridad a partir del directorio del ejecutable, el nombre del repositorio o el prompt del shell.
- Marca claramente las etapas sin firma y con firma ad hoc en lugar de ocultarlas bajo un padre reconocible.
- Si no existe una autoridad firmada reconocible, indica que la ejecución no tiene una identidad de editor local verificada.

Un proceso puede tener varias firmas en sentido conceptual: la firma del paquete de aplicación, la de un ayudante anidado y la del intérprete. Revisa el ejecutable que aparezca realmente en la lista de procesos. Una aplicación firmada puede iniciar un ayudante firmado por separado. Un ayudante puede iniciar un binario instalado por el usuario. Cada caso plantea una pregunta de identidad distinta.

## Algunas cadenas de lanzamiento deben reducir la confianza

No siempre existe una cadena limpia de padre e hijo, y los casos en que falta no son detalles excepcionales. Es ahí donde los sistemas de aprobación suelen cometer los peores errores de atribución.

### La ejecución remota rompe la ascendencia local

Si un agente local invoca SSH, tu Mac puede identificar el cliente SSH local y sus padres. El comando remoto tiene un árbol de procesos distinto en otro equipo. No digas que un `agent-cli` remoto fue iniciado por un IDE local a menos que recopiles y verifiques pruebas en ese equipo remoto.

La tarjeta de aprobación local aún puede ser útil. Puede nombrar al solicitante local, el destino, la cuenta remota si se conoce y el comando exacto que se pasó a SSH. Eso basta para decidir si el agente local puede iniciar la conexión. No basta para establecer la identidad del proceso remoto.

### El trabajo separado necesita una nueva decisión de identidad

Un proceso hijo puede llamar a `setsid`, hacer doble fork o pedir a un supervisor que continúe el trabajo después de que termine su iniciador original. A partir de ahí, una aprobación vinculada solo a la terminal o al shell inicial se convierte en ficción. El trabajador separado necesita su propio registro de proceso, su propia hora de inicio y un alcance de aprobación nuevo, salvo que exista una relación de supervisor diseñada deliberadamente que los revisores puedan inspeccionar.

### La herencia del entorno puede cambiar el actor sin cambiar el árbol

La misma ruta `node` puede cargar un paquete de agente diferente porque cambiaron `PATH`, `NODE_OPTIONS`, las rutas de módulos específicas del lenguaje, la configuración del proxy o el directorio de trabajo. La ascendencia del proceso no muestra todos los valores heredados. Captura para la investigación una huella de contexto limitada: ruta del ejecutable, argumentos, directorio actual, nombres y valores seleccionados del entorno después de eliminar datos sensibles, y un resumen del ejecutable cuando sea apropiado para tu modelo.

No vuelques un entorno completo en una tarjeta de aprobación ni en un diario. Los entornos suelen contener tokens, destinos y rutas personales. El objetivo es distinguir ejecuciones, no crear otro almacén de secretos en los registros.

## El revisor necesita una afirmación breve y pruebas que pueda inspeccionar

Un prompt de aprobación solo dispone de unos segundos para captar la atención. Coloca arriba la afirmación relevante para la decisión y después ofrece al revisor una forma de inspeccionar por qué el sistema la formuló.

Una tarjeta práctica podría decir:

```text
El proceso del agente solicita una llamada HTTP

Solicitante
agent-cli --workspace /Users/maya/src/payments
PID 81234, iniciado a las 14:08:31

Lanzado mediante
npm run agent > node scripts/launch-agent.js
desde un proceso de editor firmado

Destino
api.example.internal / POST /deployments
```

Es lo bastante compacta para leerla y lo bastante específica para cuestionarla. El revisor puede rechazarla porque el espacio de trabajo es incorrecto, el ejecutor de tareas es inesperado, el destino sorprende o la solicitud proviene de una etapa sin firma. Son motivos reales para detener una acción.

Conserva todas las pruebas detrás de la tarjeta. Guarda el PID y la hora de inicio del solicitante, la ruta del ejecutable, los argumentos, la cadena de padres, los datos de firma de la autoridad seleccionada y la decisión de aprobación. Si el mismo agente realiza otra solicitud durante la ejecución aprobada, el registro debe dejar claro que la decisión de sesión se aplicó a la vida de ese proceso, no a un nombre de aplicación separado de cualquier proceso.

La autorización por sesión de Sallyport se basa en ese límite práctico: la primera llamada de un proceso de agente nuevo solicita aprobación, y la aprobación dura solo hasta que ese proceso termina. Su tarjeta de aprobación comienza con la autoridad de firma del proceso, lo que proporciona al revisor un punto de partida reconocible sin fingir que la firma explica cada comando hijo.

## Revoca según la vida del proceso y haz explícitas las excepciones

Una aprobación de sesión debe seguir la vida del proceso del agente porque ese proceso es la unidad que hizo la solicitud. Reutilizar una aprobación porque la terminal sigue abierta, una ventana del IDE sigue visible o una tarea tiene el mismo nombre crea un alcance de aprobación que nadie puede inspeccionar honestamente.

Esta regla sí crea cierta fricción con los envoltorios de corta duración. Un gestor de paquetes puede iniciar un proceso de agente nuevo para cada subcomando. No lo soluciones ampliando discretamente la aprobación a cada futuro hijo de npm o a todos los procesos de una terminal. Decide si el flujo de trabajo necesita un host de agente estable y firmado, un proceso de mayor duración que exponga una identidad clara o una confirmación por llamada para la credencial sensible.

La aprobación por llamada es la respuesta correcta cuando el riesgo está en cada uso y no en la identidad de lanzamiento. Un token de despliegue en producción, una clave privada SSH que llega a un equipo sensible o una API administrativa con capacidad de escritura pueden merecer una decisión humana cada vez, aunque la identidad del proceso sea conocida. Reconocer el proceso reduce la confusión. No elimina la necesidad de decidir.

La prueba de implementación es sencilla: termina el proceso aprobado, inicia de nuevo el mismo comando y confirma que la segunda ejecución necesita su propia aprobación. Después inicia un comando diferente en la misma pestaña de terminal y confirma que no puede heredar la decisión de la primera ejecución. Si alguna prueba falla, el sistema aprobó una ubicación o una etiqueta en lugar de un actor.

El revisor nunca debería tener que aceptar el nombre de una terminal como sustituto de la identidad del proceso. Conserva la cadena, identifica la ejecución que solicita, muestra la autoridad reconocible más sólida que puedas defender y deja visibles las pruebas que falten. Es menos llamativo que una regla amplia de «IDE de confianza». También es lo que resiste cuando un script de tareas, un envoltorio del shell o un salto remoto resulta ser la parte importante.
