Saltar para o conteúdo

Tickets e delegação

Um ticket é a unidade de trabalho que o Control Center acompanha. É deliberadamente um artefacto estúpido: um título, um corpo, um estado, um responsável e um lugar numa árvore. Não é aquilo contra o que os agentes executam — o trabalho dos agentes vive nas conversas e o resultado estruturado de uma execução vive no registo de execução.

A atribuição regista a propriedade; não despacha nada

Secção intitulada “A atribuição regista a propriedade; não despacha nada”

Este é o ponto mais importante de todos a acertar em relação aos tickets.

Definir assignedAgentId escreve um campo e publica um TicketAssigned evento de domínio. Esse evento é um sinal de auditoria e de notificação e um disparador de pipeline. A atribuição a um agente não inicia nenhuma execução. Não existe nenhum despachante de tickets que vigie assignedAgentId e acorde esse agente.

A exceção é uma equipa: quando assignedTeamId está definido, o TeamRoutingService despacha o líder da equipa com o protocolo de funcionamento e a lista de membros.

Se quiser que um agente específico trabalhe num ticket, peça-lho numa conversa, ou faça com que um passo de pipeline o despache ou outro agente lhe delegue. Atribuir um ticket a um agente e esperar é a única coisa que nunca produzirá trabalho.

Um ticket pode ser puramente local ou estar suportado por um gestor externo, e transporta ambos na mesma linha.

O espelho — fornecedor, chave externa, URL, título, descrição, prioridade, etiquetas, estado em bruto e normalizado, marcas temporais — é uma cache quando o ticket é remoto. Aí, o remoto é a fonte de verdade e uma atualização reescreve o espelho por completo.

A sobreposição — atribuição, delegação, ligação ao espaço, ticket ascendente, projeto, PRs associadas — é exclusiva do Control Center. O gestor externo nada sabe sobre ela e uma atualização nunca lhe toca.

É esta separação que torna a sincronização minimamente segura: uma recolha do fornecedor pode ser destrutiva para o espelho sem nunca perder os metadados de orquestração que lhe estão sobrepostos.

O grafo do ciclo de vida vincula os agentes, não o utilizador

Secção intitulada “O grafo do ciclo de vida vincula os agentes, não o utilizador”

Os estados seguem backlog → open → inProgress → inReview → done, com blocked como ciclo lateral e failed / cancelled como os outros estados terminais.

O grafo de transições é imposto apenas no percurso do agente e da automatização — ferramentas MCP e reconciliadores. Uma transição ilegal aí é registada e ignorada, de modo que um agente confuso não consegue fazer recuar um ticket para fora de um estado terminal.

Todas as alterações feitas pelo utilizador na interface passam force: true e contornam o grafo por completo. Uma pessoa pode mover um ticket para qualquer estado que o seletor apresente — incluindo reabrir um estado terminal. O seletor não oferece failed; os agentes definem-no através de fail_ticket. O grafo é uma barreira para a automatização, e não uma regra sobre a forma como o trabalho pode avançar.

O TicketWorkflowService detém todas as mutações através de um único ponto de passagem obrigatório que carrega a linha, confirma que esta pertence ao espaço de trabalho de quem chama e escreve com um version esperado, de modo que duas edições simultâneas não se conseguem sobrescrever em silêncio.

Para a tabela completa de estados, as transições, a lista de campos e os tipos de relação, consulte Ciclo de vida de tickets.

Todos os tickets têm um originKind. A enumeração tem cinco valores — manual, pipelineStep, agentDelegation, externalSync, recovery — mas hoje só dois são escritos: manual (a predefinição em createTicket, incluindo os filhos delegados) e externalSync (o motor de sincronização, numa recolha do fornecedor). O TicketWorkflowService.createTicket não aceita um argumento de origem, de modo que pipelineStep, agentDelegation e recovery nunca chegam a uma linha.

A delegação é o ponto em que um agente cria trabalho para outro agente e é o sítio onde os privilégios poderiam escapar, por isso as barreiras vivem no servidor, num ponto de passagem único, e não em instruções do prompt.

O delegate_task passa pelo TicketWorkflowService.delegateGuarded, que calcula a profundidade e a raiz do filho a partir da cadeia de ascendentes e depois executa as quatro verificações de DelegationGuards: um salto que excederia um limite de profundidade de 3, um salto de regresso a um agente que já está na cadeia (deteção de ciclos), um limite máximo de autonomia (o nível efetivo do delegado no espaço não pode exceder o do delegador) e um envelope orçamental (a delegação debita o orçamento de paragem forçada restante do delegador e não pode criar orçamento novo). A configuração de produção fornece as duas portas de resolução. A autonomia e o orçamento são ignorados quando from_agent_id é omitido (um orçamento ilimitado / sem política também passa). Uma recusa é lançada como DelegationRefusedException, cuja mensagem chega ao agente delegador palavra por palavra — uma negação bem audível, nunca uma operação silenciosa sem efeito.

O delegate_ticket é o irmão mais antigo e sem barreiras: chama createTicket diretamente e ignora todas as verificações. Prefira o delegate_task.

Não confunda o limite de profundidade da delegação (3, nas cadeias de delegate_task) com o limite de encaixe de subagentes do harness integrado (2). São limites diferentes aplicados a mecanismos diferentes.

As relações de ascendência e de sub-ticket vivem em tickets.parent_ticket_id, gerido por setParent / clearParent. Não são linhas de ticket_links, por isso tudo o que leia as relações de um ticket tem de combinar as duas fontes.

O ticket_links guarda as arestas direcionais entre tickets que de outro modo não teriam relação:

Tipo de ligação Significado
blocks A origem bloqueia o destino
relatesTo Os dois tickets estão relacionados (simétrica)
duplicateOf A origem é um duplicado do destino

Um projeto é um agrupamento de tickets com âmbito de espaço de trabalho em torno de um objetivo comum. Os projetos são exclusivos do Control Center e nunca são sincronizados com um fornecedor externo.

As linhas de TicketCollaborator são a forma de associar mais do que um principal a um ticket, com uma função de assignee, collaborator ou reviewer. Pessoas e agentes são linhas em pé de igualdade — um principalId mais um collaboratorType — sem valor sentinela para «o utilizador» e sem chave externa para qualquer das tabelas.

O ticket do Control Center é o principal. Eliminar um ticket é apenas local, por isso um ticket suportado por um fornecedor que elimine não é eliminado no gestor externo e pode reaparecer na sincronização seguinte. Os colaboradores e os tickets filhos são eliminados em cascata.

As credenciais dos adaptadores são resolvidas a partir dos armazenamentos de credenciais do servidor (uma chave do Linear ou um token do GitHub colado nas Definições é detetado no pedido seguinte; o ambiente apenas semeia esses armazenamentos).

Nenhum nó de pipeline cria, conclui ou cancela tickets — a paleta não tem esse passo e o motor não chama o TicketWorkflowService. Um disparador de pipeline pode, ainda assim, ser acionado por TicketAssigned, TicketCompleted, TicketFailed ou TicketCancelled (esses quatro são os eventos de ticket que o EventPayloadMapper realmente mapeia). Não existe nenhuma coluna pipelineRunId no ticket; o acoplamento com pipelines vive no registo de execução do agente.