Saltar para o conteúdo

Ciclo de vida de tickets

Estado Valor armazenado Descrição
backlog backlog Capturado, ainda não triado para a fila ativa
open open Triado e pronto para ser trabalhado, ainda não iniciado
inProgress inProgress A ser trabalhado ativamente
blocked blocked Iniciado, mas bloqueado por algo externo
inReview inReview Trabalho concluído, à espera de revisão
done done Concluído com êxito (terminal)
failed failed Terminado com um erro (terminal)
cancelled cancelled Abandonado (terminal)

Os valores de estado são armazenados em camelCase (inProgress, inReview), nunca in_progress. Um valor armazenado nulo é interpretado como open; um valor desconhecido lança uma exceção em vez de ser coagido.

O tryParseLoose aceita aliases fornecidos pelos agentes, ignorando a distinção entre maiúsculas e minúsculas e - / _ / espaço:

Estado Aliases aceites
open todo, ready, pending, new
inProgress doing, started, active, wip
blocked stuck
inReview review, reviewing
done closed, complete, completed, finished
failed error, errored
cancelled canceled, abandoned, dropped
backlog ──→ open ──→ inProgress ──→ inReview ──→ done
│ │ │
│ └──→ failed ┘
└──→ blocked ──→ inProgress
Any non-terminal status ──→ cancelled; inReview ──→ failed

Este grafo vincula apenas os agentes e a automatização. O TicketWorkflowService.transitionStatus verifica-o quando force é false — as ferramentas MCP e os reconciliadores. Uma transição ilegal aí é registada e ignorada. Todas as alterações de estado feitas pelo utilizador na interface passam force: true e contornam o grafo por completo, pelo que uma pessoa pode mover um ticket para qualquer estado que o seletor apresente (todos os estados exceto failed), incluindo reabrir um estado terminal. Os agentes marcam failed com fail_ticket.

Um estado pode transitar sempre para si próprio. Os estados terminais não têm arestas de saída.

De Destinos permitidos
backlog open, cancelled
open inProgress, blocked, cancelled
inProgress blocked, inReview, done, failed, cancelled
blocked inProgress, cancelled
inReview inProgress, done, failed, cancelled
done — (terminal)
failed — (terminal)
cancelled — (terminal)

Armazenadas como um número inteiro na escala nativa 0–4 do Linear. Um valor desconhecido é interpretado como none.

Prioridade Armazenada como
none 0
urgent 1
high 2
medium 3
low 4

Um valor armazenado desconhecido ou nulo é interpretado como local.

Fornecedor Estado
local Armazenado inteiramente na base de dados do espaço de trabalho; a predefinição
linear Gerido remotamente, espelhado localmente

Os adaptadores de sincronização vivem em packages/cc_infra/lib/src/tickets/sync/ para o Linear, o GitHub Issues, o Jira Cloud e o ClickUp. O GitHub Issues é apenas um fornecedor de adaptador de sincronização (vendorId: github) — não é um valor de TicketProvider, pelo que um issue do GitHub obtido por recolha é armazenado com originKind: externalSync e provider: local predefinido. O mapeamento de fornecedores vive em ticket_sync_links.

O createTicket escreve manual. Uma recolha de um fornecedor escreve externalSync. Os filhos delegados permanecem manual.

Valor Significado
manual Predefinição no createTicket (linhas humanas, de agentes e delegadas)
externalSync Definido pelo TicketSyncEngine numa recolha de um fornecedor

O agregado abrange dois aspetos na mesma linha. O espelho (fornecedor, chaves, título, descrição, prioridade, etiquetas, estados, marcas temporais) é uma cache para um fornecedor remoto e é reescrito por completo por uma atualização. A sobreposição (atribuição, delegação, espaço, ascendente, projeto, PRs associadas) é exclusiva do Control Center e uma atualização nunca lhe toca.

Campo Tipo Descrição
id String UUID v4
workspaceId String Espaço de trabalho que detém o ticket
provider TicketProvider Backend que detém os dados canónicos (predefinição local)
externalKey String? Chave nativa do fornecedor (por exemplo, LIN-123). O createTicket local escreve aqui um UUID separado, não o id da linha
url String? URL Web no fornecedor remoto
title String Resumo curto; não pode estar vazio
description String? Corpo Markdown mais longo
priority TicketPriority Predefinição none
labels List<String> Etiquetas de formato livre
status TicketStatus Estado normalizado canónico
rawStatus String? O nome do estado nativo do fornecedor remoto, mantido para uma apresentação sem perdas
externalSyncedAt DateTime? Última sincronização multi-fornecedor; nulo até à primeira. O mapeamento por fornecedor vive em ticket_sync_links
parentTicketId String? Ascendente na árvore de delegação / decomposição
projectId String? Projeto que detém o ticket; nunca é enviado para um remoto
assignedAgentId String? Id do principal atribuído (apenas metadados — atribuir não despacha nada)
assigneeType PrincipalType Que tipo de principal o assignedAgentId nomeia — user ou agent (predefinição agent)
createdByType PrincipalType? Tipo de principal que criou o ticket (user ou agent); nulo para linhas antigas ou de sistema
createdById String? Id do principal que criou
assignedTeamId String? Equipa atribuída (apenas metadados)
delegatedByAgentId String? Agente que delegou este ticket
delegationDepth int Profundidade na cadeia de delegação; a raiz é 0, verificada em relação ao limite máximo de profundidade
delegationRootTicketId String? Raiz da cadeia de delegação; nulo para um ticket raiz
spaceId String? Conversa da qual o ticket derivou, ou o espaço do agente que delegou
errorMessage String? Definido quando status é failed
linkedPrIds List<String> Ids dos nós de PR aos quais este ticket está associado
metadata Map<String, dynamic> Saco de formato livre
createdAt DateTime Hora de criação
startedAt DateTime? Quando o trabalho começou
blockedAt DateTime? Quando o ticket foi bloqueado
cancelledAt DateTime? Quando o ticket foi cancelado
completedAt DateTime? Quando o ticket foi concluído com êxito
finishedAt DateTime? Quando o ticket atingiu um estado terminal
updatedAt DateTime Última mutação (atualização do espelho ou alteração da sobreposição)
version int Contador de concorrência otimista, incrementado a cada mutação
originKind TicketOriginKind Como o ticket foi criado (predefinição manual)
collaborators List<TicketCollaborator> Hidratado pelo repositório a pedido

Derivados: isTerminal, isRemote (provider != local), isAssignedToUser e displayKey (externalKey ?? id — por isso, os tickets locais apresentam o UUID separado criado no momento da criação, e não o id da linha).

As arestas armazenadas são linhas direcionais em ticket_links:

Tipo Valor armazenado Significado
blocks blocks A origem bloqueia o destino
relatesTo relates_to Relação simétrica
duplicateOf duplicate_of A origem é um duplicado do destino

O vocabulário que a interface e o ticket_relation utilizam é TicketRelationKind, derivado de uma aresta armazenada e da extremidade em que o sujeito se situa:

Tipo Derivado de
blockedBy Uma linha blocks em que o sujeito é o destino
blocking Uma linha blocks em que o sujeito é a origem
relatedTo Uma linha relates_to (simétrica)
duplicateOf Uma linha duplicate_of em que o sujeito é a origem
duplicatedBy Uma linha duplicate_of em que o sujeito é o destino
subIssueOf tickets.parent_ticket_id no sujeito
parentOf tickets.parent_ticket_id no outro ticket

As ligações ascendente e de sub-ticket não são linhas de ticket_links — vivem em tickets.parent_ticket_id.

Estas oito classes vivem em packages/cc_domain/lib/core/domain/events/ticketing_events.dart. Não existe nenhum TicketStarted, TicketDelegated, TicketCollaboratorAdded nem ExternalTicketWebhookReceived.

Evento Disparado quando
TicketCreated O ticket é criado
TicketAssigned O ticket é atribuído a um principal ou a uma equipa
TicketCompleted O trabalho termina com êxito
TicketFailed O trabalho falha
TicketCancelled O ticket é cancelado
TicketStatusChanged Qualquer alteração de estado
TicketReassigned O ticket é reatribuído
TicketDetailsUpdated O título, a descrição, a prioridade ou as etiquetas mudam

O TicketAssigned é 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 a vigiar o assignedAgentId. A atribuição a uma equipa é a exceção: o TeamRoutingService despacha o líder da equipa. Adicionar um colaborador escreve uma linha e não publica nada. Um webhook de um fornecedor é aplicado através do TicketSyncEngine sem qualquer evento de domínio.

O TicketWorkflowService detém todas as mutações.

Mecanismo Detalhe
Ponto de passagem único Todas as mutações passam por _mutate, que carrega a linha e chama _assertWorkspace antes de aplicar
Isolamento do espaço de trabalho O _assertWorkspace lança WorkspaceMismatchException quando o workspaceId da linha carregada não corresponde ao de quem chama
Concorrência otimista O _mutate escreve com expectedVersion definido para o version que acabou de ler e tenta novamente perante uma ConcurrencyConflictException, relendo o estado atualizado
Reclamação exclusiva tryCheckout / releaseCheckout; um segundo agente que reclame um ticket já reservado recebe uma CheckoutConflictException
Barreira de transição canTransitionTo, aplicado apenas quando force é false