# Qual processo real do agente está pedindo aprovação?

Um prompt de aprovação que diz «Terminal» ou «zsh» costuma ser vago demais para embasar uma decisão real. Esses programas podem fazer parte do caminho de inicialização, mas muitas vezes são apenas componentes compartilhados. O revisor precisa saber qual execução do agente pediu a ação, o que a iniciou e se existe algo nessa cadeia que dê à solicitação uma identidade duradoura.

A dificuldade é que uma árvore de processos responde a várias perguntas diferentes ao mesmo tempo. Ela pode mostrar quem criou cada processo. Pode revelar um executor de tarefas que ninguém esperava. Pode mostrar que uma IDE gráfica iniciou o terminal. Mas, sozinha, não prova que um pai assinado aprovou cada linha de código que um filho executará. Um bom desenho de aprovação usa a árvore como evidência e então formula a afirmação limitada que ela consegue sustentar.

## A aprovação pertence a um ator, não a um terminal

O ator aprovado pelo revisor é a execução do processo que solicitou a ação, interpretada no contexto de sua cadeia de inicialização. Uma janela de terminal é um lugar onde processos são executados. Ela não é automaticamente o programa responsável.

Essa distinção parece excessivamente detalhista até que uma equipe tenha vários agentes rodando no mesmo shell. Um desenvolvedor abre um terminal no iTerm2, executa um agente de programação interativo em uma aba, inicia uma tarefa do repositório em outra e deixa um monitor em segundo plano rodando em uma terceira. Os três descendentes podem ter o mesmo aplicativo de terminal em algum ponto acima deles. Chamar cada solicitação de «iTerm2» diz quase nada ao revisor.

O erro oposto também é comum. O revisor vê `node`, `python` ou `zsh` e presume que o binário mais próximo da solicitação é a identidade completa. Esse binário pode ser o interpretador de um pacote de agente, um hook de gerenciador de pacotes, um script temporário gerado por uma extensão da IDE ou um wrapper adicionado por uma ferramenta da equipe. O nome é tecnicamente correto e operacionalmente inútil.

Use três rótulos internamente, mesmo que o cartão de aprovação mostre apenas dois:

- **Solicitante**: o processo que realmente pediu para usar o canal protegido.
- **Cadeia de execução**: os processos pai relevantes que explicam como o solicitante chegou até ali.
- **Autoridade reconhecível**: o ancestral mais próximo cuja identidade o revisor consegue reconhecer razoavelmente e cuja assinatura de código pode ser avaliada.

Esses rótulos resolvem um problema que muitos sistemas de aprovação confundem. O solicitante responde «qual processo fez esta chamada?». A autoridade reconhecível responde «por qual pessoa ou organização esta execução passou?». Eles estão relacionados, mas não são intercambiáveis.

Por exemplo, uma solicitação pode se parecer com isto:

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

Se `agent-cli` fizer uma solicitação HTTP, ele será o solicitante. O shell e o npm explicam o caminho de inicialização. O processo assinado Code Helper pode ser a melhor autoridade reconhecível, desde que esteja realmente na ancestralidade ativa e não apenas aberto na área de trabalho. Um cartão que diga «agent-cli, iniciado pelo Code Helper» é honesto. Um que diga apenas «Code Helper quer acesso» elimina a informação que distinguiria essa execução de outra extensão ou tarefa.

## Uma árvore de processos é evidência, não uma declaração de intenção

Um processo pai prova que criou um filho ou herdou uma relação com ele. Não prova que o pai compreendeu os argumentos posteriores do filho, seu prompt, as instruções do repositório ou uma resposta remota.

Essa limitação importa quando as pessoas usam a linguagem de assinatura sem cuidado. A Apple descreve o designated requirement como o mecanismo usado pelo macOS para determinar se o código continua sendo o mesmo entre versões. Em geral, ele incorpora um identificador e uma autoridade de assinatura. A Apple também deixa claro o limite: código sem assinatura não tem um designated requirement duradouro, e assinaturas locais ad hoc não oferecem uma identidade estável entre versões.

Isso é uma evidência útil para aprovação. Não é uma aprovação da ação. Uma IDE assinada pode iniciar um script sem assinatura do repositório. Um terminal assinado pode executar um binário baixado. Um executor de tarefas assinado pode passar variáveis de ambiente hostis para um interpretador perfeitamente comum. A assinatura permite que o revisor reconheça o editor do código. Ela não torna segura a cadeia abaixo dele.

Trate as afirmações a seguir de maneiras diferentes:

| Afirmação | O que a sustenta | O que ela não sustenta |
|---|---|---|
| «Esta solicitação veio do PID 81234.» | O registro local do processo | Quem escreveu o código ou por que ele fez a chamada |
| «Este filho foi iniciado por este pai.» | O PID do pai e a ancestralidade ativa | Que o pai aprovou o comportamento atual do filho |
| «Este executável é assinado por esta autoridade.» | A inspeção da assinatura e o designated requirement | Que o executável é benigno ou está agindo conforme a intenção |
| «Esta execução começou nesta IDE ou terminal.» | Uma cadeia de ancestrais ativa e sem interrupções | Que a janela visível iniciou todos os descendentes |

A consequência é prática. Não reduza as quatro afirmações a um ícone simpático de aplicativo e uma única frase. O revisor deve conseguir ver qual afirmação está aceitando.

Quando a cadeia for ambígua, diga isso. «Iniciado por um script sem assinatura a partir de uma sessão de terminal» é um rótulo de aprovação melhor do que atribuir a solicitação ao aplicativo de terminal, como se ele tivesse escrito o código. A precisão falsa ensina as pessoas a ignorar o cartão.

## Inspecione a cadeia ativa antes de projetar o cartão de aprovação

A maneira mais rápida de encontrar o ator certo é inspecionar uma execução real enquanto ela ainda está ativa. Faça isso com o agente realizando uma operação inofensiva, porque a cadeia pode mudar quando o trabalho passa da inicialização para um executor de tarefas ou auxiliar filho.

No macOS, comece pelo PID do solicitante, se o seu gateway o registrar. Este comando mostra o processo, seu PID pai, o horário de início, o nome do executável e os argumentos:

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

Um resultado representativo tem este formato:

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

Depois, suba pela cadeia. O `ps` não faz a recursão por você, então uma pequena função de shell torna a inspeção repetível:

```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"
```

A saída é propositalmente simples. Procure mudanças de tipo: executável do agente para interpretador, interpretador para gerenciador de pacotes, gerenciador de pacotes para shell, shell para terminal ou auxiliar da IDE. Não pare ao reconhecer um nome. Continue até chegar a um processo que forneça uma procedência significativa para o revisor ou até a cadeia local terminar.

Para uma visão geral, esta consulta ajuda quando existem vários descendentes:

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

Use-a para investigação, não para um prompt de aprovação. Uma listagem completa de processos contém detalhes irrelevantes demais e pouca estrutura. Sua implementação deve coletá-la quando necessário e depois reduzi-la ao solicitante, ao pai imediato, à autoridade reconhecível e às etapas intermediárias que expliquem uma inicialização surpreendente.

Há duas armadilhas de tempo aqui. Primeiro, os IDs de processo podem ser reutilizados depois que um processo termina. Registre o horário de início junto com o PID e resolva a cadeia no momento da solicitação, em vez de confiar em uma amostra anterior. Segundo, um executor de tarefas pode criar um worker e terminar antes que a chamada protegida ocorra. Se o pai já tiver desaparecido, preserve a cadeia observada desde a criação do processo ou informe que falta um elo. Não o substitua por um pai presumido.

## O VS Code muda o caminho de inicialização sem se tornar o solicitante

O terminal integrado do VS Code pode injetar argumentos ou variáveis de ambiente ao iniciar shells compatíveis para habilitar a integração com o shell. A documentação também informa que a injeção automática não cobre todas as configurações, incluindo alguns subshells, sessões SSH e casos de shell complexos. Isso é um bom lembrete de que os metadados da interface do terminal e a relação de parentesco Unix são fontes de evidência diferentes.

Em um caso simples, a cadeia é assim:

```text
Visual Studio Code
  └─ terminal helper
      └─ login shell
          └─ agent-cli
```

O auxiliar do terminal existe porque a IDE precisa gerenciar um pseudoterminal e seu shell filho. O shell de login pode carregar um perfil que altera o `PATH`, ativa um runtime de linguagem, instala hooks de shell ou inicia um script específico do projeto. Esses detalhes explicam por que o mesmo comando se comporta de maneira diferente em um terminal independente e no terminal da IDE.

A decisão de aprovação não deve fingir que esses detalhes não existem. Mostre o executável do agente como solicitante. Apresente o VS Code como autoridade de inicialização somente quando ele estiver na ancestralidade atual. Inclua o shell e o comando da tarefa em um caminho expansível quando eles diferenciarem a execução de maneira relevante.

Uma falha comum é esta:

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

Uma implementação ingênua vê `zsh` e rotula a solicitação como «terminal do VS Code». Outra vê `python3` e a chama de «Python». Nenhuma ajuda o revisor a descobrir se esta é a tarefa conhecida do repositório ou um processo arbitrário iniciado pelo mesmo shell.

A afirmação útil é mais parecida com esta:

```text
Requester: python3 tools/agent.py
Run path: make test-agent > scripts/bootstrap-agent
Launched from: Visual Studio Code integrated terminal
```

Esse rótulo tem um custo: ele revela que a solicitação veio de código controlado pelo repositório. E deve revelar. Se o revisor confia em um editor assinado, mas não no checkout atual, o cartão precisa deixar espaço para essa avaliação.

Não deduza que o VS Code é o responsável com base apenas em sequências de escape do terminal, variáveis de ambiente ou no título de uma janela. Esses elementos podem ser herdados, copiados ou estar desatualizados. A ancestralidade ativa é uma evidência mais forte. O contexto fornecido pela IDE pode enriquecer a exibição depois que você confirmar essa ancestralidade, mas não substituí-la.

## Um terminal independente fornece contexto, não autoridade geral

iTerm2, Terminal e aplicativos semelhantes dão ao usuário um ponto de entrada deliberado. Se um desenvolvedor abre um terminal limpo, digita um comando de agente e recebe imediatamente um prompt de aprovação, identificar o aplicativo de terminal é um contexto útil. Isso informa onde a execução começou.

Ainda assim, isso não justifica aprovar indiscriminadamente todo filho que compartilhe a sessão. Shells são feitos para criar filhos, e abas de longa duração frequentemente permanecem abertas depois que o motivo de sua abertura desapareceu. Um comando iniciado às 9h em uma aba de terminal pode deixar um worker rodando até o almoço, depois de um `cd`, uma troca de branch, uma alteração no ambiente e três comandos sem relação.

Torne a distinção visível:

```text
Requester: agent-cli --workspace /Users/maya/src/payments
Parent: zsh -l
Interactive origin: signed terminal application
Session started: Tue Jul 21 14:08:31 2026
```

A origem interativa é um contexto de apoio. A sessão pertence à execução real do processo. Se o processo terminar, revogue a aprovação da sessão mesmo que a aba do terminal continue aberta. Um novo processo de agente na mesma aba é uma nova execução, com novos argumentos e possivelmente um executável diferente no `PATH`.

É aqui que a identidade reconhecível pelo revisor fica complicada. O aplicativo de terminal pode ser assinado por um editor conhecido, enquanto `agent-cli` é um executável sem assinatura instalado em um diretório do projeto. O cartão honesto deve mencionar os dois fatos. Não transfira a assinatura do terminal para o filho sem assinatura, como se o terminal tivesse dado seu aval.

O mesmo problema aparece com funções e aliases de shell. Um comando que parece `agent` pode se expandir para uma função que muda de diretório, carrega um arquivo de ambiente, inicia um script de pacote e finalmente executa outro binário. Seu coletor de processos verá apenas os filhos resultantes. Se quiser o comando digitado como contexto opcional, capture-o pela integração com o shell ou por um hook de shell e rotule-o como histórico de comando inserido pelo usuário, não como identidade do processo.

## Scripts e executores de tarefas criam as cadeias mais enganosas

Executores de tarefas são populares porque transformam uma configuração local complicada em um comando fácil de lembrar. Essa conveniência muitas vezes faz suas cadeias de processos parecerem mais confiáveis do que realmente são.

Considere esta inicialização comum:

```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 processo intermediário tem uma finalidade legítima. `just` seleciona uma receita. `/bin/sh` interpreta o corpo da receita. npm seleciona um script de pacote e inicia um shell. Node avalia o script de inicialização. Ainda assim, nenhum desses nomes informa ao revisor se a solicitação protegida veio de uma execução conhecida do agente ou de outro comando que por acaso usou o mesmo runtime.

Não remova as camadas intermediárias do registro de auditoria. É exatamente nelas que uma investigação encontrará um script de pacote alterado, um hook de ciclo de vida `pre` ou `post` inesperado, um `PATH` modificado ou um script do repositório que substituiu outro executável. Reduza essas camadas na exibição de aprovação somente depois de preservá-las em um registro de eventos imutável.

Uma regra de exibição sensata é:

1. Coloque primeiro o executável e os argumentos do solicitante.
2. Mostre o iniciador direto quando ele mudar a interpretação, como `npm run deploy-agent` ou `make migration-bot`.
3. Identifique o primeiro ancestral assinado reconhecível como autoridade de inicialização.
4. Mantenha toda a ancestralidade observada disponível nos detalhes do evento.

Essa regra evita dois hábitos ruins. O primeiro é ocultar todos os wrappers, o que torna invisível uma tarefa modificada. O segundo é mostrar uma árvore de sete linhas antes que o revisor consiga responder à solicitação. Quando as pessoas recebem cartões de aprovação ilegíveis, aprovam o formato do cartão, não a ação.

Rejeito uma recomendação: aprovar o executor de tarefas em vez do agente porque o nome da tarefa é mais fácil de reconhecer. Nomes de tarefas costumam ser texto do repositório. Qualquer pessoa que possa alterar o checkout talvez consiga alterar o que a tarefa faz. Use o nome da tarefa como contexto, mas vincule a sessão de aprovação à execução observada do processo do agente e reavalie quando uma nova execução começar.

## Escolha cuidadosamente a fronteira assinada

O ancestral assinado mais próximo costuma ser a melhor autoridade reconhecível, mas «mais próximo» exige julgamento. Um shell ou interpretador de sistema assinado pode estar diretamente acima de um script sem assinatura. Chamar esse shell de autoridade produz um rótulo tecnicamente assinado que não informa quem forneceu o código.

As orientações de assinatura de código da Apple distinguem o identificador de um executável de sua identidade de assinatura e usam um designated requirement para estabelecer a identidade do código. A ferramenta `codesign` pode mostrar esse requisito. Use a saída para identificar um aplicativo ou auxiliar assinado, mas não trate apenas uma string de identificador como prova de uma relação com o editor.

Para um aplicativo empacotado, inspecione o executável ou bundle do aplicativo que aparece na ancestralidade:

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

Um resultado típico contém campos neste formato:

```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"
```

Os campos exatos variam conforme a assinatura e o caminho de distribuição. O importante é que seu sistema de aprovação capture uma identidade de código estável quando o macOS puder fornecê-la e depois mostre uma versão compreensível para o revisor, sem ocultar as evidências brutas.

Use estas regras ao escolher a autoridade exibida:

- Prefira um aplicativo gráfico assinado ou um auxiliar dedicado assinado que esteja realmente na ancestralidade do solicitante.
- Não use um interpretador genérico assinado como substituto do script sem assinatura que ele executou.
- Não deduza autoridade pelo diretório do executável, pelo nome do repositório ou pelo prompt do shell.
- Marque claramente as etapas sem assinatura e com assinatura ad hoc, em vez de escondê-las abaixo de um pai reconhecível.
- Se não houver uma autoridade assinada reconhecível, diga que a execução não tem uma identidade verificada de editor local.

Um processo pode ter várias assinaturas em sentido conceitual: a assinatura do bundle do aplicativo, a assinatura de um auxiliar aninhado e a assinatura de um interpretador. Inspecione o executável que realmente aparece na lista de processos. Um aplicativo assinado pode iniciar um auxiliar assinado separadamente. Um auxiliar pode iniciar um binário instalado pelo usuário. Cada um é uma pergunta de identidade diferente.

## Algumas cadeias de inicialização devem reduzir a confiança

Uma cadeia limpa de pai e filho nem sempre está disponível, e os casos ausentes não são detalhes excepcionais. É neles que os sistemas de aprovação tendem a cometer os piores erros de atribuição.

### A execução remota rompe a ancestralidade local

Se um agente local invocar SSH, seu Mac poderá identificar o cliente SSH local e seus pais. O comando remoto terá uma árvore de processos própria em outro host. Não diga que um `agent-cli` remoto foi iniciado por uma IDE local, a menos que você colete e verifique evidências nessa máquina remota.

O cartão de aprovação local ainda pode ser útil. Ele pode informar o solicitante local, o destino, a conta remota, se conhecida, e o comando exato passado ao SSH. Isso basta para decidir se o agente local pode iniciar a conexão. Não basta para estabelecer a identidade do processo remoto.

### Trabalho separado exige uma nova decisão de identidade

Um filho pode chamar `setsid`, fazer um segundo fork ou pedir a um supervisor que continue o trabalho depois que seu iniciador original terminar. Quando isso acontece, uma aprovação vinculada apenas ao terminal ou ao shell inicial vira ficção. O worker separado precisa de seu próprio registro de processo, seu próprio horário de início e um escopo de aprovação novo, a menos que você tenha projetado deliberadamente uma relação com um supervisor que os revisores possam inspecionar.

### A herança do ambiente pode mudar o ator sem mudar a árvore

O mesmo caminho de `node` pode carregar um pacote de agente diferente porque `PATH`, `NODE_OPTIONS`, caminhos de módulos específicos da linguagem, configurações de proxy ou o diretório de trabalho foram alterados. A ancestralidade do processo não mostra todos os valores herdados. Capture uma impressão digital de contexto limitada para investigação: caminho do executável, argumentos, diretório atual, nomes e valores selecionados do ambiente depois da remoção de dados sensíveis e um resumo do executável quando isso fizer sentido para o seu modelo.

Não despeje um ambiente inteiro em um cartão de aprovação ou diário. Ambientes frequentemente carregam tokens, endpoints e caminhos pessoais. O objetivo é distinguir execuções, não criar um segundo cofre de segredos nos logs.

## O revisor precisa de uma afirmação curta e evidências inspecionáveis

Um prompt de aprovação tem poucos segundos para conquistar a atenção. Coloque a afirmação relevante para a decisão no topo e ofereça ao revisor um caminho para inspecionar por que o sistema chegou a essa conclusão.

Um cartão prático poderia dizer:

```text
Agent process requests an HTTP call

Requester
agent-cli --workspace /Users/maya/src/payments
PID 81234, started 14:08:31

Launched through
npm run agent > node scripts/launch-agent.js
from a signed editor process

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

Isso é compacto o bastante para ser lido e específico o bastante para ser contestado. O revisor pode rejeitá-lo porque o workspace está errado, o executor de tarefas é inesperado, o destino surpreende ou a solicitação veio de uma etapa sem assinatura. Esses são motivos reais para interromper uma ação.

Mantenha todas as evidências atrás do cartão. Preserve o PID e o horário de início do solicitante, o caminho do executável, os argumentos, a cadeia de pais, os dados de assinatura da autoridade escolhida e a decisão de aprovação. Se o mesmo agente fizer outra solicitação posteriormente na execução aprovada, o registro deve deixar claro que a decisão da sessão se aplicou à vida daquele processo, não a um nome de aplicativo desvinculado de qualquer processo.

A autorização por sessão do Sallyport parte desse limite prático: a primeira chamada de um novo processo de agente pede aprovação, e a aprovação dura apenas até esse processo terminar. O cartão de aprovação começa pela autoridade de assinatura do código do processo, oferecendo ao revisor um ponto de partida reconhecível sem fingir que a assinatura explica cada comando filho.

## Revogue pelo ciclo de vida do processo e torne as exceções explícitas

Uma aprovação de sessão deve acompanhar o ciclo de vida do processo do agente, porque o processo é a unidade que fez a solicitação. Reutilizar a aprovação porque um terminal continua aberto, uma janela da IDE continua visível ou uma tarefa tem o mesmo nome cria um escopo que ninguém consegue inspecionar honestamente.

Essa regra cria algum atrito para wrappers de curta duração. Um gerenciador de pacotes pode iniciar um novo processo de agente para cada subcomando. Não corrija isso ampliando silenciosamente a aprovação para todos os futuros filhos do npm ou para todos os processos de um terminal. Decida se o fluxo precisa de um host de agente estável e assinado, de um processo mais duradouro que exponha uma identidade clara ou de confirmação por chamada para a credencial sensível.

A aprovação por chamada é a resposta certa quando o risco está em cada uso, não na identidade da inicialização. Um token de implantação em produção, uma chave privada SSH que alcança um host sensível ou uma API administrativa com permissão de escrita podem exigir uma decisão humana a cada uso, mesmo quando a identidade do processo é conhecida. Reconhecer o processo reduz a confusão. Não elimina a necessidade de julgamento.

O teste de implementação é simples: encerre o processo aprovado, inicie o mesmo comando novamente e confirme que a segunda execução precisa de sua própria aprovação. Depois, inicie um comando diferente na mesma aba do terminal e confirme que ele não pode herdar a decisão da primeira execução. Se algum teste falhar, o sistema aprovou um local ou um rótulo em vez de um ator.

O revisor nunca deveria precisar tratar o nome de um terminal como substituto da identidade do processo. Preserve a cadeia, identifique a execução que fez a solicitação, mostre a autoridade reconhecível mais forte que você consegue defender e mantenha visível qualquer evidência ausente. Isso é menos atraente do que uma regra ampla de «IDE confiável». Também é o que continua funcionando quando um script de tarefa, um wrapper de shell ou um salto remoto se revela a parte realmente importante.
