Multijogador — identidade, associação e presença
O Control Center funciona para um único operador, mas o modelo por baixo dele é multiagente desde o início. Humanos e agentes conversam nos mesmos espaços, editam os mesmos tickets e aparecem na mesma lista de participantes. Tornar isso seguro e compreensível se resume a três decisões: quem conta como ator, o que controla o acesso e qual estado é compartilhado de forma durável e qual é apenas transmitido ao vivo.
Principals: usuários e agentes são atores em pé de igualdade
Seção intitulada “Principals: usuários e agentes são atores em pé de igualdade”Tudo o que registra “quem fez isto” — mensagens, tickets, revisões, planos,
logs de execução, entradas de auditoria — é resolvido por meio de um
Principal, uma união selada de UserPrincipal e AgentPrincipal com uma
forma compacta no protocolo (user:<id> / agent:<id>). A atribuição nunca
colapsa N humanos (ou N agentes) em uma única conta sentinela, então uma
revisão deixada por um agente e uma revisão deixada por uma pessoa são
distinguíveis em todo lugar em que aparecem.
Como o principal é a unidade de identidade, ele também é a unidade de contenção:
- Os limites de taxa são por principal, não por sessão. A proteção contra flood nas chamadas remotas de ferramentas é orçada por usuário, não por conexão: todos os dispositivos de um membro consomem uma mesma janela deslizante compartilhada (120 chamadas por minuto, das quais no máximo 30 podem alterar estado), então abrir três dispositivos não triplica a cota de ninguém e um membro barulhento não deixa os outros sem cota.
- O desfazer é por principal. O diário de ações de cada cliente registra apenas as mutações que o próprio principal executou. Você pode dar ⌘Z na sua própria edição de ticket; não pode desfazer a de um colega — quando você a vê, a faixa durável já a resolveu (veja abaixo).
A associação é a fronteira de acesso — não a chave de pareamento
Seção intitulada “A associação é a fronteira de acesso — não a chave de pareamento”Duas credenciais separadas respondem a duas perguntas separadas. A chave de pareamento (veja Controle remoto e mobile) autentica um dispositivo no servidor. A associação autoriza o usuário desse dispositivo a agir dentro de um espaço de trabalho. Um dispositivo pareado cujo usuário não tem associação em um espaço de trabalho não consegue nem ler nem alterar esse espaço.
A associação é uma linha por (espaço de trabalho, usuário), guardada no arquivo de banco de dados do próprio espaço de trabalho — então “quem está neste espaço de trabalho?” é uma leitura comum com escopo, enquanto a pergunta inversa, “em quais espaços de trabalho eu estou?”, necessariamente atravessa arquivos e é um dos poucos fan-outs entre espaços de trabalho permitidos. É o que o seletor de espaços de trabalho executa antes de qualquer espaço ter sido escolhido.
Cada linha carrega uma função de uma escada de cinco níveis, ordenada por precedência:
| Função | Pode |
|---|---|
owner |
Controle total, incluindo a exclusão do espaço de trabalho; exatamente um por espaço de trabalho |
admin |
Gerencia membros, convites, configurações e operações destrutivas |
member |
Leitura e escrita do dia a dia: mensagens, tickets, execuções de agente |
viewer |
Somente leitura em todo o espaço de trabalho; globs de exclusão de segredos ocultam os caminhos correspondentes nas superfícies que contêm código |
guest |
O mesmo piso de somente leitura do viewer, no posto mais baixo da escada |
Toda decisão de acesso se reduz a “a função de quem chama é pelo menos X”. O
piso é derivado do tipo da operação, a menos que ela declare um
explicitamente: uma leitura exige guest, uma mutação exige member, uma
operação destrutiva exige admin. ACLs por objeto além das concessões por
repositório descritas abaixo ficam deliberadamente fora de escopo.
Os convites são a forma de conceder associação, em Configurações → Espaço
de trabalho → Membros. Um admin emite um código de uso único (como link ou QR)
que expira em sete dias por padrão; o servidor guarda apenas o hash SHA-256 do
código, e o código em si é mostrado uma única vez. Resgatá-lo provisiona o
usuário na hora, registra a associação com a função do convite (nunca owner
— a propriedade é transferida explicitamente, nunca concedida por convite) e
passa para o pareamento de dispositivo. As credenciais de dispositivo emitidas
após o pareamento têm prazo de 30 dias, então um pareamento não é uma chave
permanente.
As concessões por repositório ficam abaixo da associação. Como o servidor
mantém checkouts completos, a associação ao espaço de trabalho por si só nunca
pode ter mais privilégios do que o forge: toda superfície que contém código —
arquivos, diffs, revisão de PR, busca de código — verifica a concessão do
membro naquele repositório. As concessões são por (espaço de trabalho,
usuário, repositório) e classificadas como none → read → review →
write, em que write significa comandar execuções de agente que gravam no
repositório. A ausência de linha significa none; proprietários e admins têm
implicitamente write em todo repositório vinculado e não carregam linha
alguma.
Duas faixas de estado de colaboração
Seção intitulada “Duas faixas de estado de colaboração”O Control Center divide o estado compartilhado em duas faixas com regras de durabilidade opostas, porque os dois tipos de estado falham de maneiras opostas.
A faixa efêmera de presença
Seção intitulada “A faixa efêmera de presença”A presença — quem está online, onde está, o que está digitando, o que um
agente está fazendo neste instante — não vale nada no momento em que fica
desatualizada. Se fosse persistida, toda reconexão teria de reconciliar “o
banco de dados diz que Sam está editando este ticket” com a realidade. Por
isso a presença nunca é gravada no banco de dados. Ela vive em um hub de
presença na memória do servidor: os clientes publicam com presence.update,
os pares se inscrevem com presence.watch e as entradas expiram
automaticamente após três sinais perdidos (os sinais ocorrem a cada 10
segundos; o hub varre a cada 5). Não há nada a reconciliar — só deixar expirar.
Duas propriedades mantêm a faixa confiável:
- A identidade vem da sessão, nunca dos argumentos do cliente. O servidor
mapeia o dispositivo autenticado da conexão para um id de usuário; um cliente
só consegue atualizar a própria presença, então ninguém pode falsificar o
status ou o locus de outro participante. Uma atualização
offlineexplícita (desconexão ou o alternador de invisibilidade) remove a entrada na hora. - A lista é centralizada no servidor. Os pares nunca trocam presença diretamente, então todo participante vê a mesma lista, seja qual for a topologia. Agentes não têm cliente, então o servidor sintetiza as entradas deles a partir do estado das execuções ao vivo — pensando, executando, bloqueado em um portão de aprovação, concluído — com uma única execução representativa decidindo o estado e o locus de um agente para que execuções simultâneas não façam a lista oscilar.
O fan-out é estratificado por consumidor: desktop e web usam o nível completo (no máximo uma emissão da lista a cada 100 ms), o celular um nível de resumo (a cada 500 ms), para que atualizações na cadência do cursor não derretam o PWA.
O locus de um participante — onde ele está agora, até o nível do artefato —
é uma união selada: Space, File (repositório + caminho + linha opcional),
Pr, Ticket, PlanNode. Nem todo locus tem para onde levar: não existe
rota de visualizador de arquivos, então um locus File é renderizado como
rótulo, e não como destino. A faixa também carrega reivindicações leves
(“Sam está editando este ticket”, “um agente detém este worktree”):
visibilidade de conflito, não bloqueios. Tipos de locus desconhecidos são
interpretados como null, então um cliente antigo simplesmente não renderiza o
que não entende.
A faixa durável compartilhada
Seção intitulada “A faixa durável compartilhada”Os registros que precisam sobreviver — tickets, o documento de Notas compartilhado do espaço, mensagens — seguem o caminho oposto: autoritativos, ordenados e persistidos. Edições simultâneas são resolvidas por vitória do último a escrever na ordem de recebimento no servidor, nunca por relógios de cliente. A resolução é deliberadamente granular: as edições de ticket são por coluna, então duas pessoas editando campos diferentes do mesmo ticket se aplicam e nenhuma sobrescreve a outra (as transições de fluxo de trabalho mantêm bloqueio otimista); o documento de Notas do espaço é uma única coluna, então o documento inteiro é LWW, com as reivindicações leves na faixa de presença tornando a edição simultânea visível em vez de bloqueada. Ele deliberadamente não é um CRDT — o ônus da prova fica com o CRDT, e o LWW por ordem de recebimento mais reivindicações visíveis cobre os padrões reais de colaboração.
“Último a escrever” significa ordem de recebimento no servidor, nunca um relógio de cliente — um notebook com relógio desajustado não consegue vencer uma edição que perdeu. Por trás da faixa durável está o feed de sincronização: gatilhos do SQLite acrescentam cada mutação a um log de alterações por espaço de trabalho dentro da transação de escrita, alocando um número de sequência monotônico por espaço de trabalho na mesma transação da própria mutação. Um serviço do servidor acompanha esse log e o transforma em frames delta versionados, na ordem da sequência. Os clientes evoluem de snapshots completos para deltas; uma lacuna na sequência faz aquele store voltar ao modo de snapshot em vez de aplicar frames incorretamente.
Seguir, orientar, assumir o controle, devolver o controle
Seção intitulada “Seguir, orientar, assumir o controle, devolver o controle”A faixa de presença não é só uma lista — ela comanda os verbos de colaboração:
- Modo seguir. Clique em um participante e seu cliente acompanha o locus dele, navegando conforme ele navega. Seguir vale “até você agir”: no momento em que você navega por conta própria, o seguir se desfaz. Um banner dispensável avisa quando outro participante está destacando (apresentando) um espaço em que você está.
- Orientar. Uma mensagem para um agente em execução entra na fila como orientação; o agente a relê antes de retomar o próximo turno, então você pode orientar uma execução em andamento sem matá-la.
- Assumir o controle. Assumir o controle de uma conversa pausa toda execução ativa no próximo limite de turno livre (uma execução do Claude Code, que não tem fronteira segura, é interrompida) respaldada por um snapshot do git a cada turno, de modo que a assunção é reversível. Um marcador durável bloqueia novos despachos para o espaço e sobrevive a uma reinicialização do servidor — o servidor nunca retoma automaticamente uma edição inacabada de uma pessoa. Um editor ativo por worktree é uma reivindicação leve: uma segunda assunção de controle é visível e recusada, nunca mesclada em silêncio.
- Devolver o controle. Devolver o controle publica no espaço um resumo estruturado de diff do worktree, o enfileira como orientação para as execuções pausadas, retoma essas execuções e limpa o marcador.
O dial de autonomia
Seção intitulada “O dial de autonomia”Orientar e assumir o controle são intervenções feitas depois do fato. O dial de autonomia é a configuração permanente: por espaço, por agente, definida no cabeçalho do espaço, ele decide o que acontece quando um agente chega a uma ação sob portão.
| Nível | Valor no protocolo | Efeito em uma chamada de ferramenta sob portão |
|---|---|---|
| Somente propor | proposeOnly |
Negada de imediato, com um motivo que o agente deve repassar — ele pode descrever a ação, mas não executá-la |
| Agir com aprovação | actWithApproval |
O portão de aprovação que falha fechado: uma confirmação é apresentada a um operador conectado, e sem aprovador a resposta é negada |
| Agir livremente | actFreely |
Pré-aprovada, exceto quando se aplica uma regra de barreira deny rígida — uma regra de negação sempre vence |
| (não definido) | null |
O mesmo que agir com aprovação: o padrão do servidor |
Duas coisas ficam fora do dial nas duas direções. O verbo de saída de um modo
é fixo e nunca pode ser negado, diga o que disser o dial — o modo orchestrate
já foi, estruturalmente, incapaz de emitir seu único entregável porque uma
predefinição negava a classe de efeito que esse verbo declara. E um deny
rígido das barreiras de ação não pode ser sobreposto por actFreely.
Modo solo sem regressão
Seção intitulada “Modo solo sem regressão”Tudo isso é aditivo. O fluxo de trabalho de quem opera sozinho não muda: com uma única pessoa e nenhum agente, o hub de presença não tem o que transmitir, então a faixa emite o snapshot inicial e depois fica ociosa, com tráfego zero; a máquina de LWW e de sincronização é o mesmo caminho de gravador único de sempre; e a chave de pareamento continua sendo a credencial de dispositivo que sempre foi. A associação já é a fronteira de acesso — com uma única pessoa há uma só linha de proprietário e nenhum elemento de interface da lista.
Conceitos relacionados
Seção intitulada “Conceitos relacionados”- Espaços de trabalho e isolamento: a fronteira que a associação aplica
- Controle remoto e mobile: o lado da identidade com a chave de pareamento e a credencial de dispositivo
- O modelo de agentes: a metade agente da união de principals
- Ciclo de vida do despacho de agentes: as execuções que orientar e assumir o controle pausam e retomam
- Barreiras: a política de classes de efeito com que o dial de autonomia se compõe
- Eventos de domínio: o barramento por onde trafegam as atualizações ao vivo