Pular para o conteúdo

Conversas de PR

Uma pull request já é o lugar onde a conversa sobre o código acontece. As conversas de PR a tornam uma superfície em um espaço de trabalho do Control Center, do mesmo modo que uma ponte de chat torna o Slack uma: mencione com @ o bot do GitHub App do servidor em uma PR, responda dentro das threads de revisão dela, ou adicione o rótulo ai-review, e o trabalho acontece no espaço de revisão da PR — sob a mesma atribuição e as mesmas barreiras que qualquer coisa digitada no app. Um comentário também exige associação; o rótulo não, porque não tem autor. As respostas do bot voltam no GitHub, no mesmo canal em que a pergunta foi feita.

Três decisões moldam o recurso, e cada uma é a mesma decisão que a ponte de chat tomou para o chat, reafirmada para uma forge: em que direção a conexão flui, o que autoriza um comentário que chega de fora e a quem cada lado é atribuído.

O único canal de envio que o GitHub oferece a um app é um webhook, e um webhook precisa de uma URL de entrada — justamente a única coisa que esta superfície deliberadamente nunca exige. Então a descoberta é uma varredura: a cada minuto mais ou menos, uma busca com alias por instalação do app pede ao GitHub as PRs que mencionam o bot (ou seu slug curto) e as PRs abertas que carregam o rótulo de revisão; as PRs que já têm um espaço de revisão têm suas threads de comentários lidas, e tudo é comparado por id de comentário com o que foi visto na varredura anterior.

As consequências de projeto merecem ser nomeadas:

  • A latência é um intervalo de varredura, não um evento. A confirmação de recebimento 👀 chega em cerca de um minuto. Nada urgente depende dela.
  • Exatamente uma vez, por comentário. Os ids de comentário são únicos em todo o GitHub; cada um é registrado no momento em que é classificado como elegível, antes que qualquer coisa aja sobre ele. Uma falha pode perder o tratamento de um comentário, mas nunca dispará-lo duas vezes.
  • O tempo offline é recuperado, não reproduzido. O conjunto de vistos é persistido, então um reinício entrega o que chegou enquanto ele estava fora do ar. A única exceção é a primeiríssima varredura: sem nenhuma memória com a qual comparar, tudo o que está pendente é registrado em silêncio — um operador que rotulou vinte PRs antes de este servidor existir não pode voltar e encontrar vinte revisões em execução.
  • O rótulo é um conjunto, não um evento. A busca informa a presença do rótulo, então uma PR rotulada dispara uma única vez, e nunca mais. Adicionar de novo um rótulo depois de removê-lo é invisível para a varredura; uma menção é o caminho para executar novamente.

A busca roda uma vez por conta de instalação, no cliente do próprio proprietário, porque um token de instalação só enxerga os repos da própria instalação — uma única credencial para todo o servidor deixaria passar em silêncio todas as PRs das outras instalações do app.

Um comentário de PR chega carregando pouco mais que o login de um autor — uma identidade externa não autenticada até que se prove o contrário. O bot o resolve contra um índice reverso dos membros do espaço de trabalho e das contas do GitHub conectadas a eles, reconstruído conforme um TTL, porque as pessoas entram e são convidadas enquanto o servidor está rodando.

Uma recusa segura se parece com uma escada, e cada degrau responde na thread em que recusou:

  • Um login desconhecido — não vinculado, ou vinculado por alguém que não é membro deste espaço de trabalho — é instruído a entrar e a se certificar de que pertence ao espaço
  • Um membro somente leitura (viewer ou guest) é informado de que seu papel não pode iniciar trabalho

Os dois casos de login desconhecido compartilham uma mesma resposta: o índice reverso só retorna um membro deste espaço de trabalho, então uma conta não vinculada e uma conta vinculada que não é membro são o mesmo erro de busca. Nenhum deles prossegue. Um comentário nunca é atribuído a alguém que não o enviou, e as palavras de um estranho nunca entram em um espaço de trabalho que ele não pode ver.

O rótulo ai-review não tem autor a resolver, então não sobe por essa escada. Adicioná-lo inicia a revisão no espaço de trabalho que já tem o repositório vinculado.

Cada lado da conversa carrega uma identidade diferente, e a diferença é o recurso:

  • No GitHub, o bot é o servidor. Cada comentário, reação e resposta que a ponte publica trafega com o token de instalação do app. Ninguém clicou em nada em um cliente, então nada toma emprestada a conta de uma pessoa — a mesma regra que mantém webhooks e polling longe de credenciais humanas.
  • No espaço, a pergunta é sua. A mensagem é atribuída ao membro ao qual o autor foi resolvido, e a execução do agente roda em nome dele: os commits o reconhecem como coautor, e o dial de autonomia e as barreiras de ação do espaço se aplicam exatamente como a uma mensagem digitada no app. As gravações na forge feitas pela execução ainda saem como o app — a mesma regra do item acima —, e não com o token pessoal do membro.

Publicar as constatações da revisão de volta no GitHub continua sendo uma decisão de uma pessoa, tomada no Control Center. Uma menção pode iniciar uma revisão; só um humano publica uma.

Tudo o que é de autoria de uma conta de bot é descartado antes que qualquer filtro rode — o nosso especificamente, e qualquer sufixo [bot], para que o bot de revisão de outro app também não possa acordar o nosso. Sem isso, as próprias respostas do bot chegariam como comentários novos e o disparariam de novo para sempre.

O sufixo [bot] também é o motivo de a forma curta existir. O GitHub fixa o login da conta de um app em <slug>[bot], não oferece autocomplete para ele e nunca resolve @slug para o app — então aceitar o slug puro é uma decisão do nosso mecanismo de correspondência, e descobri-lo exige uma faixa de busca no texto bruto dos comentários, em vez do índice de menções do GitHub. O custo é declarado, e não amenizado: um usuário humano que compartilhe o slug do app aciona o bot a cada menção em forma curta a si mesmo. Nomes de app distintos tornam a colisão hipotética.

  • Nenhuma publicação automática. As constatações chegam ao espaço de revisão; publicar no GitHub é uma ação humana separada
  • Nenhuma identidade difusa. O autor se resolve em exatamente um membro ou é recusado — não há atribuição por suposição
  • Nenhum webhook do GitHub App. Esta superfície nunca registra um, então não há URL de entrada a expor para comentários de PR. Os outros webhooks do produto (pipelines, sincronização de tickets) são uma configuração separada e opcional.