Pular para o conteúdo

Tickets e delegação

Um ticket é a unidade de trabalho que o Control Center acompanha. Ele é, de propósito, um artefato burro: um título, um corpo, um status, um responsável e um lugar em uma árvore. Ele não é aquilo contra o que os agentes executam — o trabalho dos agentes vive nas conversas, e a saída estruturada de uma execução fica no log de execução.

A atribuição registra a propriedade; ela não despacha nada

Seção intitulada “A atribuição registra a propriedade; ela não despacha nada”

De todos os pontos sobre tickets, este é o mais importante de acertar.

Definir assignedAgentId grava um campo e publica um TicketAssigned evento de domínio. Esse evento é um sinal de auditoria e de notificação e um gatilho de pipeline. Atribuir um ticket a um agente não inicia nenhuma execução. Não existe um despachante de tickets que observe assignedAgentId e acorde esse agente.

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

Se você quer que um agente específico trabalhe em um ticket, você pede a ele em uma conversa, ou uma etapa de pipeline o despacha, ou outro agente delega a ele. Atribuir um ticket a um agente e esperar é a única coisa que nunca vai produzir trabalho.

Um ticket pode ser puramente local ou respaldado por um rastreador externo, e ele carrega os dois na mesma linha.

O espelho — provedor, chave externa, URL, título, descrição, prioridade, rótulos, status bruto e normalizado, carimbos de data e hora — é um cache quando o ticket é remoto. O sistema remoto é a fonte da verdade nesse caso, e uma atualização reescreve o espelho por inteiro.

A sobreposição — atribuição, delegação, vínculo com espaço, pai, projeto, PRs vinculadas — existe só no Control Center. O rastreador não sabe nada a respeito dela, e uma atualização nunca a toca.

É essa separação que torna seguro sincronizar: uma importação do fornecedor pode ser destrutiva para o espelho sem nunca perder os metadados de orquestração sobrepostos.

O grafo de ciclo de vida vincula os agentes, não você

Seção intitulada “O grafo de ciclo de vida vincula os agentes, não você”

Os status seguem backlog → open → inProgress → inReview → done, com blocked como um ramo lateral e failed / cancelled como os demais estados terminais.

O grafo de transições é aplicado apenas no caminho do agente e da automação — ferramentas MCP e reconciliadores. Uma transição ilegal por ali é registrada em log e ignorada, de modo que um agente confuso não consegue fazer um ticket voltar para trás e sair de um estado terminal.

Toda mudança feita por um usuário na interface passa force: true e ignora o grafo por completo. Uma pessoa pode mover um ticket para qualquer status que o seletor liste — inclusive reabrir um estado terminal. O seletor não oferece failed; os agentes definem esse status por meio de fail_ticket. O grafo é uma barreira de proteção para a automação, não uma regra sobre como o trabalho pode andar.

O TicketWorkflowService concentra toda mutação em um único ponto de passagem, que carrega a linha, verifica se ela pertence ao espaço de trabalho de quem chama e grava com um version esperado, de modo que duas edições simultâneas não possam se sobrescrever em silêncio.

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

Todo ticket tem um originKind. O enum tem cinco valores — manual, pipelineStep, agentDelegation, externalSync, recovery — mas hoje só dois são gravados: manual (o padrão em createTicket, incluindo filhos delegados) e externalSync (o motor de sincronização, em uma importação do fornecedor). TicketWorkflowService.createTicket não recebe um argumento de origem, então pipelineStep, agentDelegation e recovery nunca chegam a uma linha.

A delegação é onde um agente cria trabalho para outro agente e é o ponto por onde privilégios poderiam vazar, então as verificações ficam do lado do servidor, em um ponto único de passagem, e não nas 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 pais e então executa as quatro verificações de DelegationGuards: um salto que ultrapassaria um limite de profundidade de 3, um salto de volta a um agente que já está na cadeia (detecção de ciclo), um teto de autonomia (o nível efetivo do delegado no espaço não pode exceder o do delegador) e um envelope de orçamento (a delegação debita o orçamento de parada rígida restante do delegador e não pode criar mais). A montagem de produção fornece ambas as portas de resolução. Autonomia e orçamento são ignorados quando from_agent_id é omitido (orçamento ilimitado / sem política também passa). Uma recusa é lançada como uma DelegationRefusedException cuja mensagem chega ao agente delegador literalmente — uma negação ruidosa, nunca uma operação silenciosa sem efeito.

O delegate_ticket é o irmão mais antigo e sem verificações: ele chama createTicket diretamente e pula 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 aninhamento de subagentes do harness integrado (2). São limites diferentes, em mecanismos diferentes.

As relações de pai e de subitem ficam em tickets.parent_ticket_id, controladas por setParent / clearParent. Elas não são linhas de ticket_links, então qualquer coisa que leia as relações de um ticket precisa combinar as duas fontes.

ticket_links guarda as arestas direcionais entre tickets que, fora isso, não têm relação entre si:

Tipo de vínculo Significado
blocks A origem bloqueia o destino
relatesTo Os dois tickets estão relacionados (simétrico)
duplicateOf A origem é uma duplicata do destino

Um projeto é um agrupamento de tickets com escopo de espaço de trabalho em direção a um objetivo comum. Projetos existem só no Control Center e nunca sincronizam com um provedor externo.

As linhas de TicketCollaborator são como mais de um principal é anexado a um ticket, em um papel de assignee, collaborator ou reviewer. Pessoas e agentes são linhas equivalentes — um principalId mais um collaboratorType — sem valor sentinela para “o usuário” e sem chave estrangeira para qualquer uma das tabelas.

No Control Center, o ticket é o principal. Excluir um ticket é somente local, então um ticket respaldado por um fornecedor que você exclui não é excluído no rastreador e pode reaparecer na próxima sincronização. Colaboradores e tickets filhos são excluídos em cascata.

As credenciais dos adaptadores são resolvidas nos armazenamentos de credenciais do servidor (uma chave do Linear ou um token do GitHub colado em Configurações é reconhecido na próxima requisição; o ambiente apenas semeia esses armazenamentos).

Nenhum nó de pipeline cria, conclui ou cancela tickets — a paleta não tem essa etapa e o motor não chama o TicketWorkflowService. Um gatilho de pipeline ainda pode disparar em TicketAssigned, TicketCompleted, TicketFailed ou TicketCancelled (esses quatro são os eventos de ticket que o EventPayloadMapper de fato mapeia). Não existe uma coluna pipelineRunId no ticket; o acoplamento com pipelines vive no log de execução do agente.