# Processos MCP órfãos ainda podem deixar trabalho para trás

Uma falha do cliente não informa se o servidor MCP parou. Ela informa apenas que um processo morreu, ou pelo menos deixou de responder. A diferença importa porque stdio é um meio de transporte, não um interruptor que encerra o trabalho que já atravessou uma fronteira entre processos.

Já vi equipes tratarem uma pilha de processos auxiliares antigos como um problema de manutenção e depois descobrirem que um deles ainda mantinha um token de nuvem, um socket de controle SSH ou uma fila de trabalho que ninguém havia associado à execução que falhou. O comando de limpeza era fácil. A parte que haviam ignorado era reconstruir quem autorizou a última ação.

A resposta correta tem três tarefas separadas: identificar com precisão o processo restante, provar se ele ainda tem um caminho para agir e preservar o registro que conecta suas últimas chamadas à sessão responsável por elas. Faça isso nessa ordem. Encerrar primeiro pode eliminar o incômodo, mas também pode apagar a melhor evidência do que aconteceu.

## Um processo sem pai é uma pista, não um veredito

Um processo órfão é um processo-filho cujo pai original terminou e cujo pai no sistema operacional mudou, muitas vezes para o PID 1. Isso é uma evidência útil, mas não define um servidor MCP inseguro. Supervisores de processos, shells, IDEs e serviços de inicialização podem reparentar processos-filhos saudáveis durante o funcionamento normal.

Para um servidor MCP stdio, faça uma pergunta mais específica: esse processo ainda pertence a uma conexão ativa do cliente ou sobreviveu depois que o cliente responsável por seu stdin e stdout desapareceu? A resposta combina a árvore de processos com descritores abertos, tempo decorrido e um registro de atividade.

A documentação do Model Context Protocol descreve stdio como uma integração local iniciada por processos. O cliente inicia um comando e troca JSON-RPC delimitado por novas linhas usando a entrada e a saída padrão do servidor. Esse desenho dá a um servidor bem implementado um sinal simples de fim de vida: quando a entrada chega ao EOF, o lado do cliente desapareceu.

EOF é evidência de um transporte interrompido ou fechado. Não é evidência de que todos os trabalhadores, processos-filhos, conexões de rede, temporizadores ou trabalhos remotos tenham parado. Um servidor pode receber uma chamada de ferramenta, começar o trabalho e perder o cliente antes de produzir uma resposta JSON-RPC. Se o manipulador iniciou um subprocesso ou enviou uma solicitação a um sistema remoto, esse trabalho pode ter seu próprio ciclo de vida.

É aqui que duas falhas diferentes costumam ser confundidas:

- Um **órfão do sistema operacional** perdeu o pai original ou se separou da árvore de processos esperada.
- Um **órfão lógico** perdeu a sessão que dava significado ao seu trabalho, mesmo que o PID do pai ainda pareça normal.

O primeiro pode desperdiçar memória ou manter uma porta aberta. O segundo pode causar uma alteração externa indesejada. É preciso investigar os dois.

Um processo com PPID 1, tempo decorrido antigo e sem stdin aberto é suspeito. Um processo ainda ligado a um terminal ativo também pode ser perigoso se o cliente responsável estiver travado sem deixar isso claro e o processo tiver uma credencial ou conexão reutilizável. Por outro lado, um processo que permanece depois de uma falha pode ser inofensivo se não tiver autoridade nem acesso a um canal de ação.

Não crie uma regra que diga «PPID 1 significa encerrar». Crie um registro que explique por que o processo existe, qual execução o criou, o que ele ainda pode acessar e o que foi feito com ele.

## Comece com um inventário de processos que possa ser defendido

Faça uma captura antes de enviar qualquer sinal. Depois que o processo for encerrado, você não poderá recuperar a linha de comando, o grupo de processos ou um descritor aberto. Além disso, a reutilização de PIDs transforma anotações soltas em suposições muito rapidamente.

No macOS, comece com a visão completa, em vez de usar uma linha única engenhosa que filtre justamente as evidências necessárias:

```sh
ps -axo user,pid,ppid,pgid,stat,etime,command
```

Procure os comandos iniciados pelo cliente MCP. Pode ser um binário de servidor direto, um interpretador como `node` ou `python`, um lançador de pacotes, um auxiliar SSH ou o shim `sp mcp`. Copie as linhas relevantes para uma anotação do incidente antes de restringir a busca.

Os campos respondem a perguntas diferentes:

- `pid` identifica o processo apenas para esta inspeção.
- `ppid` informa se o pai imediato ainda existe.
- `pgid` identifica o grupo de processos e muitas vezes revela auxiliares irmãos iniciados juntos.
- `stat` pode mostrar se o processo está dormindo, parado ou em um estado que não pode ser interrompido.
- `etime` mostra se uma execução supostamente recente está ativa há horas ou dias.
- `command` costuma ser o único registro restante do executável e dos argumentos usados para iniciá-lo.

Depois, inspecione cada candidato diretamente. Substitua pelo PID real e salve a saída com um carimbo de data e hora no registro do incidente.

```sh
ps -o pid=,ppid=,pgid=,stat=,etime=,user=,command= -p 48271
lsof -nP -p 48271
```

O primeiro comando fornece um registro compacto de identidade. O segundo mostra o que o processo ainda mantém aberto. Preste atenção especial aos descritores 0, 1 e 2. Um servidor stdio normal costuma ter uma extremidade de leitura para stdin e extremidades de escrita para stdout e stderr. Se stdin chegou ao EOF, o processo ainda pode exibir um descritor de pipe. Portanto, não deduza que ele está ativo apenas pela presença desse descritor. Observe o quadro completo: processo par, terminais, arquivos regulares, sockets Unix, conexões TCP e processos-filhos.

`lsof` também pode revelar um problema que as listas de processos escondem. Suponha que o pai do servidor tenha desaparecido, mas que ele ainda possua uma conexão TCP estabelecida com uma API interna. Isso não prova que ele possa fazer uma nova solicitação, mas fornece uma capacidade concreta para investigar. Se ele possui um socket de controle SSH ou um socket Unix local ligado a um auxiliar de credenciais, trate isso como uma pista, não como motivo para presumir segurança.

Verifique o grupo de processos antes de agir:

```sh
ps -axo pid,ppid,pgid,stat,etime,command | awk '$3 == 48271 || $2 == 48271'
```

Este exemplo pressupõe que `48271` seja o ID do grupo observado. Ajuste-o ao valor real. A saída pode mostrar um wrapper, um servidor e um processo auxiliar que um `kill` simples aplicado a um único PID deixaria ativos. Também pode provar que o processo não tem irmãos restantes e é mais fácil de isolar.

Evite um atalho comum: procurar apenas por `node`, `python` ou `npx` e encerrar todas as correspondências. Esses nomes são ambientes de execução, não identidades. Uma limpeza ampla pode interromper uma extensão do editor, uma execução de teste local, uma tarefa de compilação ou uma automação sem relação com o incidente. Compare a linha de comando completa com a configuração esperada do servidor e depois inspecione o pai e o grupo ao redor dele.

Se o cliente registrar o comando do servidor na inicialização, armazene esse comando junto do registro da sessão. Uma investigação posterior deixa de ser uma busca imprecisa por nome de processo e passa a ser uma comparação direta.

## O pipe stdio não define o limite da ação

Um servidor stdio bem implementado deve parar de aceitar solicitações quando stdin é fechado. Ele deve cancelar o trabalho que ainda não começou, fechar o transporte e sair depois de executar qualquer limpeza limitada. O guia oficial do SDK MCP para TypeScript destaca o mesmo ponto operacional na discussão sobre encerramento: fechar um transporte não drena automaticamente os manipuladores de ferramentas em andamento antes que o processo termine.

Essa última parte merece mais atenção. Quando um cliente falha, um manipulador de ferramenta pode estar em vários estados:

1. Ainda não iniciou trabalho externo e pode ser cancelado de forma limpa.
2. Enviou uma solicitação, mas ainda não recebeu resposta.
3. Concluiu a alteração remota, mas perdeu o caminho de resposta antes de informar o sucesso.
4. Iniciou um processo-filho local ou um trabalho remoto que sobrevive ao manipulador.
5. Está bloqueado esperando uma dependência externa e pode continuar mais tarde.

Só o primeiro estado pode ser descrito com segurança como «nada aconteceu». Os outros quatro exigem registros fora do processo morto do cliente.

Considere um exemplo concreto. Um servidor recebe uma chamada de ferramenta para implantar uma compilação. Ele grava a solicitação em uma API remota de compilação e o cliente falha enquanto a API processa o pedido. O servidor percebe que o pipe de stdout foi interrompido. Se sair imediatamente, a compilação pode continuar. Se tentar novamente sem um mecanismo de idempotência, pode iniciar uma segunda compilação. Se permanecer ativo com uma credencial de longa duração, pode consultar, tentar novamente ou iniciar trabalho adicional depois que o cliente original desapareceu.

A falha não está no fato de o servidor continuar ativo. O problema é tratar o fechamento do transporte como uma declaração completa sobre autorização, cancelamento e estado remoto.

Para cada canal de ação, defina explicitamente o limite:

- Qual evento impede a entrada de novo trabalho no servidor?
- Qual identificador permite consultar ou cancelar o trabalho já enviado?
- A operação remota aceita idempotência ou um token de solicitação?
- Qual processo ainda mantém a credencial depois que o cliente desaparece?
- Onde o resultado final será registrado se a resposta JSON-RPC não puder ser entregue?

Se você não consegue responder a essas perguntas para uma ferramenta de alto impacto, não a considere segura apenas porque usa stdio.

A mesma regra vale para SSH. Um comando com aparência interativa ainda pode ter iniciado um processo em segundo plano no host remoto. Fechar o cliente local pode fechar o canal local, enquanto um comando remoto separado do shell continua. O servidor precisa de um desenho de comando que torne o trabalho remoto observável e cancelável, não de uma expectativa otimista sobre desconexões do terminal.

## Prove se o processo restante ainda pode agir

A existência de um processo não é autoridade. É preciso testar o caminho da ação sem provocar uma ação real.

Primeiro, identifique onde está a autoridade. Um servidor que lê um token de API do próprio ambiente tem um perfil de risco diferente daquele que pede a um broker separado para executar cada ação. Um servidor com uma chave privada SSH em um arquivo tem um perfil diferente daquele que delega por meio de um auxiliar local de curta duração. Registre a resposta para o servidor específico sob análise.

Depois, inspecione o que o processo ainda mantém aberto e acessível. `lsof` é um ponto de partida, mas não revela todas as credenciais mantidas na memória nem todas as sessões autenticadas. Combine-o com seus próprios registros de ações e, quando disponível, com a trilha de auditoria do sistema externo.

Uma sequência útil de investigação é:

1. Registre o PID candidato, o comando, o PPID, o grupo de processos e os sockets de rede ou Unix abertos.
2. Encontre a ação externa mais recente associada ao processo ou à sessão.
3. Verifique se ocorreu uma nova ação depois do horário da falha do cliente.
4. Revogue ou desative o caminho de autorização do processo.
5. Observe se o processo tenta outra ação e se o gateway a rejeita.

Não faça o teste pedindo ao servidor órfão uma escrita que pareça inofensiva. Muitos sistemas não têm uma escrita realmente inofensiva, e uma chamada de teste pode alterar limites de uso, criar ruído de auditoria, mudar estado ou acionar uma automação. Prefira um endpoint de saúde ou identidade somente leitura, se o desenho tiver um. Melhor ainda, valide a negativa no gateway ou broker de credenciais depois da revogação.

Há uma distinção importante: um processo que ainda tem acesso à rede não necessariamente consegue agir, e um processo sem conexão de rede visível ainda pode conseguir agir mais tarde. Resolução DNS, um proxy, um auxiliar local, um temporizador enfileirado ou um processo-filho podem reabrir o caminho. Por isso, o teste de revogação é mais importante do que uma fotografia dos sockets.

Se o servidor mantém diretamente um segredo reutilizável, o escopo da limpeza deve incluir a rotação ou revogação do segredo. Encerrar o processo remove uma cópia da memória, mas não muda o que um invasor, uma imagem de processo capturada ou um serviço remoto pode fazer com o mesmo segredo. Equipes muitas vezes evitam a rotação porque ela dá trabalho. Isso é compreensível, mas não é um argumento de segurança.

Um modelo com gateway muda a investigação. O servidor pode sobreviver como um processo não confiável e ainda assim não conseguir fazer uma nova chamada protegida, porque nunca recebeu a credencial e já não possui uma sessão ativa aprovada. O Sallyport mantém os segredos de API e SSH em seu cofre criptografado e executa a ação por conta própria. O agente não recebe o segredo em texto simples nem um valor substituto. Isso reduz o dano que um processo stdio perdido pode causar, mas ainda é preciso revogar uma execução suspeita, em vez de presumir que uma falha do processo fez isso automaticamente.

O padrão prático é simples: depois de revogar a sessão ou o caminho da ação, uma chamada protegida tentada deve ser negada, e a negativa deve ser registrada. Sem demonstrar isso, você não confirmou a contenção.

## Registre as últimas chamadas antes que a limpeza mude a história

Um registro de incidente precisa de identificadores estáveis, não de uma narrativa montada de memória. Um PID é temporário e pode ser reutilizado. Uma linha de comando pode mudar depois de uma atualização. Um identificador de sessão e um registro de auditoria que evidencie adulteração oferecem uma âncora muito melhor.

No mínimo, crie uma linha por processo suspeito com estes campos:

```text
Observed at:
Client process PID and command:
MCP server PID and command:
Parent PID and process group:
Session identifier:
Authorization state:
Last successful action time:
Last attempted action time:
Action target and operation:
Result or remote job identifier:
Revocation time and operator:
Termination signal and exit result:
Follow-up required:
```

O par mais importante é o identificador da sessão e a última ação tentada. Muitas equipes registram apenas chamadas bem-sucedidas e escondem o momento mais revelador de uma investigação de falha: a solicitação que saiu da máquina local, mas nunca recebeu um resultado.

Mantenha três horários separados. Registre quando o cliente ficou indisponível, quando começou a última ação conhecida do servidor e quando a autoridade foi revogada. Se você usar apenas um «horário do incidente», não conseguirá saber se uma chamada ocorreu antes da falha, durante uma janela incerta ou depois que a contenção deveria estar ativa.

Preserve também o alvo da ação. «Chamou uma API de nuvem» não basta. Registre a conta ou a classe do endpoint, o método ou a categoria do comando SSH e qualquer identificador de solicitação ou trabalho que permita pesquisar o serviço remoto. Evite armazenar corpos de solicitação sensíveis em uma anotação geral de incidente. Você precisa de informação suficiente para reconciliar o efeito, não de uma segunda cópia de todos os segredos e registros de clientes.

Para um gateway com diários separados de sessão e atividade, use os dois. O registro de sessão responde quem executou o processo do agente e se essa execução ainda está autorizada. O registro de atividade responde quais chamadas individuais ocorreram e em que ordem. São perguntas diferentes, e juntá-las em um único fluxo amplo torna ambas mais difíceis de responder sob pressão.

O Sallyport projeta os dois diários a partir de um registro de auditoria criptografado e encadeado por hash. Depois de capturar as entradas relevantes de sessão e atividade, execute a verificação de integridade offline:

```sh
sp audit verify
```

Salve o resultado exato do comando junto do registro do incidente. A verificação não decide se a ação foi autorizada ou sensata. Ela informa se a cadeia de auditoria usada como evidência ainda pode ser verificada sem acesso ao cofre. Essa separação é útil quando a pessoa que revisa o incidente não deve receber credenciais apenas para conferir o histórico.

Não espere um incidente formal de segurança para praticar esse registro. Uma revisão rotineira de falha é o momento em que você encontra IDs de sessão ausentes, comandos ambíguos e logs que só existem na máquina morta. Corrigir essas lacunas durante uma limpeza tranquila custa muito menos do que descobri-las depois de uma escrita em produção.

## Revogue a autoridade antes de encerrar o processo

A ordem mais segura é revogar, verificar a negativa e só então parar o processo. Inverter a ordem parece mais rápido porque o processo desaparece na hora, mas pode deixar um registro de autorização ativo e dificultar a correlação posterior.

Comece pela revogação efetiva mais específica. Se o sistema acompanha a autorização por execução do agente, revogue essa execução. Se uma única credencial foi exposta ao servidor, desative ou faça a rotação dessa credencial. Se um trabalho remoto tem um identificador de cancelamento, cancele esse trabalho separadamente. São ações diferentes porque tratam de ciclos de vida diferentes.

Não presuma que a saída do servidor cancela o trabalho remoto. Um processo pode desaparecer enquanto a solicitação continua na fila. Também não presuma que o cancelamento remoto interrompe o servidor. Se ele ainda tiver autoridade, pode tentar novamente ou enviar outra solicitação. A contenção exige que os lados local e remoto concordem que a execução terminou.

Depois de revogar o caminho da ação, confirme o estado com evidências adequadas ao seu ambiente. Pode ser uma negativa registrada no diário de atividades do gateway, uma solicitação autenticada malsucedida a um endpoint seguro de identidade ou uma entrada de auditoria remota mostrando que o token foi desativado. O método exato varia. O princípio não: prove que um processo sobrevivente não pode fazer uma chamada protegida.

Em seguida, envie um sinal normal de encerramento ao PID identificado:

```sh
kill -TERM 48271
sleep 2
ps -p 48271 -o pid=,ppid=,stat=,etime=,command=
```

Se o último comando não retornar nenhuma linha de processo, registre esse resultado. Se o processo continuar, verifique se está sendo encerrado, bloqueado em I/O ou mantendo trabalho-filho ativo. Antes de escalar, inspecione novamente o grupo e os processos-filhos. Talvez seja necessário encerrar um auxiliar conhecido separadamente, mas não elimine um grupo inteiro às cegas sem confirmar que todos os membros pertencem à mesma execução que falhou.

Use `kill -KILL` apenas quando o encerramento normal falhar e você já tiver preservado as evidências. O SIGKILL não dá ao processo a chance de fechar arquivos, cancelar trabalho, emitir um log final ou remover estado temporário. Às vezes essa é a escolha correta. Descreva-a pelo que é: contenção forçada com possível limpeza incompleta.

O Monitor de Atividade do macOS pode ajudar quando a linha de comando não é suficiente. A Apple documenta as ações normais Quit e Force Quit, e o aplicativo pode exibir processos hierarquicamente. A visualização em árvore ajuda a confirmar relações entre pais e filhos. Ela não substitui o registro do incidente, pois uma lista gráfica não preserva as evidências de sessão e ação necessárias mais tarde.

## Transforme o encerramento do servidor em requisito de projeto

Servidores stdio devem tratar o desaparecimento do cliente como um evento de primeira classe, não como um caso raro. Falhas de clientes são comportamento normal de software. Laptops entram em repouso, terminais fecham, IDEs reiniciam, atualizações interrompem processos e agentes podem abortar depois de um erro do modelo.

O servidor deve ter um caminho explícito de encerramento com quatro propriedades. Ele para de aceitar novas solicitações quando a entrada fecha. Registra cada ação iniciada com um identificador de correlação. Dá ao trabalho ativo uma oportunidade limitada de cancelar ou chegar a um estado conhecido. Depois desse limite, sai, em vez de se tornar acidentalmente um serviço permanente em segundo plano.

Não confunda encerramento gracioso com espera infinita. Um servidor que recebe EOF e espera indefinidamente por uma API externa é um órfão mais educado. Defina um prazo para a limpeza, registre o que permanecer sem resolução e saia. O trabalho não resolvido deve ser localizável por seu ID de trabalho externo ou registro de ação.

O gerenciamento de processos-filhos é igualmente importante. Se uma ferramenta inicia um compilador, gerenciador de pacotes, auxiliar SSH, driver de navegador ou wrapper de comando, o servidor precisa saber se esse filho deve morrer quando o servidor morrer. Configure os grupos de processos de forma deliberada. Capture os PIDs dos filhos. Durante o encerramento, termine apenas os filhos pertencentes à solicitação e registre se saíram.

Evite colocar trabalho em segundo plano por meio de um shell, a menos que o contrato da ferramenta peça explicitamente um trabalho durável em segundo plano. Um comando como `some-command &` cria um segundo ciclo de vida que o servidor MCP talvez não observe. Se você precisa de trabalho durável, envie-o a um sistema de tarefas que devolva um ID e ofereça ferramentas próprias de status e cancelamento. Trabalho oculto em segundo plano não é durabilidade. É uma lacuna de auditoria.

Use idempotência quando o serviço remoto oferecer esse recurso. Dê a cada escrita externa um identificador de solicitação derivado da sessão e da chamada da ferramenta e registre esse identificador antes de enviar a solicitação. Se o cliente falhar depois do envio, será possível consultar o sistema remoto em vez de adivinhar se deve tentar novamente. Quando não houver suporte à idempotência, documente a ambiguidade e exija uma decisão de operador antes de repetir uma escrita.

O servidor também deve escrever diagnósticos em stderr, nunca em stdout. O stdout pertence ao fluxo JSON-RPC do MCP, delimitado por novas linhas. Uma linha de depuração perdida pode corromper o protocolo, provocar uma falha no cliente e criar exatamente o padrão de falha que você está tentando limpar. Parece um detalhe pequeno, até que um servidor de produção imprima um aviso de biblioteca na inicialização.

## Coloque a propriedade no registro da ação, não no histórico do shell

O histórico do shell é conveniente até desaparecer, ser truncado, compartilhado ou gravado depois que o processo morre. O registro da ação precisa carregar as informações de propriedade enquanto o trabalho acontece.

Para cada chamada que possa tocar um sistema externo, associe contexto suficiente para responder depois a quatro perguntas: qual execução do agente a solicitou, qual processo local a enviou, qual decisão de autorização a permitiu e qual operação externa resultou dela. Se qualquer uma estiver ausente, uma falha do cliente deixará uma lacuna que o investigador terá de preencher por inferência.

Não use a identidade de assinatura de código para responder a todas as perguntas de propriedade. Ela informa quem assinou o executável, o que é útil para decidir se um processo merece aprovação. Não identifica a execução individual, o prompt que levou à chamada nem a solicitação remota. Use a autoridade da assinatura para estabelecer confiança no início da sessão. Depois, use um identificador de sessão e registros por chamada para rastreabilidade operacional.

O mesmo alerta vale para as linhas de comando. Um comando pode mostrar que `sp mcp` ou um executável de servidor foi iniciado. Ele não informa com segurança qual chamada de ferramenta foi a última, se um usuário a aprovou ou se o sistema remoto a aceitou. Trate a lista de processos como evidência de apoio, não como fonte de auditoria.

Quando ocorrer uma falha, faça a correlação nesta ordem:

1. Encontre a execução do agente responsável pelo processo do cliente no horário da falha.
2. Encontre o processo do servidor ou o grupo de processos iniciado por essa execução.
3. Localize as últimas entradas de atividade dessa execução e compare os horários com a captura do processo.
4. Reconcilie qualquer operação externa inacabada usando seu identificador de solicitação, transação ou trabalho.
5. Registre os eventos de revogação e encerramento ao lado das últimas chamadas.

Essa sequência evita um erro conhecido: encontrar um PID antigo, encerrá-lo e depois atribuir sua última chamada de API à execução errada do agente porque duas sessões usaram o mesmo comando de servidor. Comandos se repetem. Registros de sessão não deveriam se repetir.

Se hoje seu ambiente não consegue fazer essa correlação, adicione essa capacidade antes de conceder acesso de escrita a ferramentas autônomas. Experimentação somente leitura pode conviver com observabilidade imperfeita. Alterações em produção não.

## Um exercício de falha revela as lacunas enquanto o risco é baixo

Faça um exercício controlado de falha para cada servidor MCP capaz de produzir alterações externas. Use uma conta de teste ou um caminho de ação somente leitura e capture as evidências como se estivesse lidando com um incidente real.

Inicie uma sessão normal do cliente e execute uma ação com um identificador de correlação conhecido. Enquanto o servidor estiver ativo, encerre o cliente abruptamente. Depois, inspecione o PID do servidor, o PPID, o grupo de processos, os descritores abertos e o diário de ações. Revogue a sessão. Confirme que uma chamada protegida posterior é negada. Por fim, encerre o servidor se ele não tiver saído ao receber EOF e reconcilie a ação externa.

O exercício deve produzir respostas, não apenas uma etiqueta de aprovado ou reprovado. Você precisa saber se o fechamento de stdin chega ao servidor, se os processos-filhos permanecem ativos, se os trabalhos remotos têm identificadores de cancelamento, se os logs identificam o responsável e se a revogação muda o comportamento imediatamente.

Preste atenção ao tempo. Uma falha que ocorre antes do envio da solicitação se comporta de maneira diferente de outra que acontece depois que um serviço remoto aceitou a solicitação. Repita o exercício nos dois pontos se o servidor oferecer instrumentação suficiente. A fronteira incômoda entre «enviado» e «confirmado» é onde vivem as escritas duplicadas e as falsas garantias.

Registre a expectativa de limpeza para cada servidor. Uma boa expectativa é específica: depois que a entrada fecha, o servidor para de aceitar chamadas, sai dentro do tempo limite configurado, não deixa processos auxiliares sob sua responsabilidade e cria um registro de atividade para cada ação externa iniciada. Uma expectativa fraca é «o cliente normalmente faz a limpeza». Comportamento normal não é controle.

Depois de repetir isso algumas vezes, processos antigos deixam de ser misteriosos. Eles se tornam um modo de falha definido, com uma captura do processo, uma trilha de propriedade, uma ação de revogação e uma regra de limpeza. Esse é o padrão a buscar. O processo pode falhar. Sua capacidade de explicar e conter suas últimas chamadas não deveria falhar.
