Eventos de domínio
Barramento de eventos
Secção intitulada “Barramento de eventos”O DomainEventBus é um barramento de publicação/subscrição com difusão e em processo. Os publicadores chamam publish(event); os subscritores consomem um stream tipado on<T>(). Todos os eventos implementam DomainEvent e transportam occurredAt.
O barramento é executado dentro do cc_server. Todos os publicadores e todos os subscritores em atividade residem aí. Nenhum cliente vê este barramento — o servidor reencaminha um subconjunto curado como frames JSON-RPC notifications/*, filtrado por subscritor com base na associação ao espaço de trabalho.
| Medida | Contagem |
|---|---|
| Classes de eventos concretas | 54 |
Ficheiros de origem (packages/cc_domain/lib/core/domain/events/) |
14 (mais domain_event_bus.dart) |
| Bases abstratas que nunca são publicadas diretamente | 1 (TaskLifecycleEvent, uma hierarquia selada) |
Todos os eventos do catálogo abaixo vivem nesse único diretório; não há eventos de domínio declarados em mais nenhum sítio.
Eventos de espaço de trabalho, de agente e de repo
Secção intitulada “Eventos de espaço de trabalho, de agente e de repo”| Evento | Payload | Emitido quando |
|---|---|---|
WorkspaceCreated |
workspaceId |
O espaço de trabalho é criado |
AgentRunCompleted |
agentId, runId?, workspaceId?, conversationId? |
O agente termina uma execução (com êxito ou com falha) |
AgentAwaitingInput |
workspaceId, kind, summary, spaceId?, conversationId?, agentId?, agentName? |
Um agente começa a aguardar por uma pessoa: uma aprovação, uma pergunta ask_user ou uma execução parada numa credencial (kind é approval, question ou credential) |
RepoAdded |
repoId, path, workspaceId |
Um repo é registado; desencadeia a indexação de código em segundo plano |
SkillUpdated |
workspaceId, slug, origin, computedHash, scanVerdict? |
Uma skill é instalada ou atualizada, transportando o veredicto da análise da cadeia de abastecimento |
Eventos de PR e de revisão
Secção intitulada “Eventos de PR e de revisão”O primeiro bloco é o ciclo de vida da própria PR. O segundo é a vigilância das PRs próprias: sinais sobre uma PR da sua autoria, cada um transportando forUserId para que a sondagem os possa encaminhar para a pessoa em causa em vez de os difundir.
| Evento | Payload | Emitido quando |
|---|---|---|
PullRequestPublished |
prId, workspaceId, repoOwner, repoName |
O agente abre uma PR |
PullRequestStatusChanged |
status, workspaceId, prId?, repoFullName?, prNumber? |
A PR é fundida/fechada/aberta/reaberta/aprovada |
PrMerged |
prId, workspaceId, agentId |
A PR é fundida — um sinal estreito, apenas de merge, mantido para análises/notificações |
PrReviewRequested |
workspaceId, repoOwner, repoName, prNumber, prTitle |
O GitHub pede a revisão do utilizador do servidor (sondagem de notificações, review_requested) |
PrMentioned |
workspaceId, repoOwner, repoName, prNumber, prTitle |
O utilizador do servidor é mencionado numa PR (sondagem de notificações, mention) |
ExternalPrMerged |
workspaceId, repoOwner, repoName, prNumber, prTitle, mergedByLogin? |
Uma PR acompanhada externamente (não criada por um agente) foi fundida, detetada pela sondagem de notificações do GitHub; corresponde à categoria de notificação prMerged |
ExternalPrDetected |
repoOwner, repoName, prNumber, prTitle, author, workspaceId? |
PR não criada por um agente encontrada por sondagem — apenas para pipelines (alimenta o external_pr_welcome), não gera qualquer notificação |
PrHeadChanged |
repoOwner, repoName, prNumber, prTitle, previousHeadSha, headSha, workspaceId |
O SHA de topo da PR mudou — o autor fez push |
ReviewBecameStale |
workspaceId, spaceId, repoOwner, repoName, prNumber, prTitle, reviewedHeadSha, headSha |
Uma revisão com IA concluída já não descreve a PR que reviu. Só é emitido quando existe uma revisão para o commit substituído, por isso mantém-se um sinal em vez de um aviso a cada push |
A vigilância das PRs próprias
Secção intitulada “A vigilância das PRs próprias”Os seis transportam workspaceId, repoOwner, repoName, prNumber, prTitle e um forUserId opcional.
| Evento | Payload adicional | Emitido quando |
|---|---|---|
PrMergeReadinessChanged |
ready, reason |
Uma PR da sua autoria ficou pronta para merge, ou deixou de estar |
PrReviewDecisionChanged |
decision, reviewersRemaining, approverLogin? |
Um revisor aprovou, pediu alterações ou teve uma aprovação anulada |
PrChecksStatusChanged |
failing, failingCheckName?, failingCheckUrl? |
A CI ficou vermelha, ou recuperou |
PrCommentMentioned |
commentId, authorLogin, bodyPreview, isReviewComment, threadId?, path?, line? |
Alguém o mencionou num comentário de PR ou num comentário de revisão |
PrThreadReplied |
commentId, authorLogin, bodyPreview, threadId?, path?, line? |
Alguém respondeu num tópico de revisão em que participa |
PrThreadResolved |
threadId, commentId?, path?, line? |
Alguém resolveu um tópico de revisão em que participa |
Eventos de mensagens
Secção intitulada “Eventos de mensagens”| Evento | Payload | Emitido quando |
|---|---|---|
MessageReceived |
spaceId, messageId, senderName, contentPreview, isAgentMessage, workspaceId?, mentions, requestedByUserId?, senderUserId? |
Chega uma nova mensagem |
SpaceCreated |
spaceId, workspaceId |
O espaço é criado |
SpaceDeleted |
spaceId, workspaceId |
O espaço é eliminado |
SpaceProvisioningChanged |
workspaceId, spaceId, status, step? |
O espaço de trabalho do espaço entra em aprovisionamento, atinge um passo (clonar um repo, fazer checkout de uma PR, configurar um agente) ou passa a pronto/falhado — a ponte de chat narra-o no respetivo cartão de tarefa |
Eventos de tickets
Secção intitulada “Eventos de tickets”| Evento | Payload | Emitido quando |
|---|---|---|
TicketCreated |
ticketId, workspaceId |
O ticket é criado |
TicketAssigned |
ticketId, ticketTitle, ticketBody?, ticketUrl?, assignedAgentId?, assigneeType, assignedTeamId?, workspaceId |
Ticket atribuído a um Principal ou a uma equipa |
TicketCompleted |
ticketId |
O trabalho termina (terminal) |
TicketFailed |
ticketId, errorMessage |
O agente falha (terminal) |
TicketCancelled |
ticketId |
O ticket é cancelado (terminal) |
TicketStatusChanged |
ticketId, workspaceId, from, to |
Qualquer alteração de estado |
TicketReassigned |
ticketId, workspaceId, fromAgentId?, toAgentId? |
O ticket é reatribuído |
TicketDetailsUpdated |
ticketId, workspaceId |
O título, a descrição ou a prioridade são alterados |
Eventos do ciclo de vida de tarefas
Secção intitulada “Eventos do ciclo de vida de tarefas”O fluxo unificado do ciclo de vida de tarefas — uma execução de agente despachada, identificada pelo id do respetivo registo de execução. Todos os eventos transportam taskId, um seq monótono por tarefa, workspaceId?, agentId? e uma phase; as linhas abaixo listam apenas os campos adicionais.
| Evento | Payload | Emitido quando |
|---|---|---|
TaskDispatched |
— | A tarefa é entregue a um backend para execução |
TaskRunning |
— | A tarefa começou a produzir saída |
TaskWaitingLocalDirectory |
lockedPath, holderTaskId? |
Tarefa em espera — outra tarefa detém o bloqueio do caminho em disco de que precisa |
TaskProgress |
note? |
Ponto de controlo de progresso a meio da execução |
TaskMessage |
messageType, content |
Mensagem transmitida em fluxo (text/thinking/tool_use/tool_result/error); comunicada na fase progress |
TaskCompleted |
summary? |
A tarefa terminou com êxito |
TaskFailed |
errorMessage |
A tarefa terminou com um erro |
TaskCancelled |
— | A tarefa foi cancelada antes de terminar |
O enum TaskPhase também tem um membro queued, mas nada publica um evento de fila — não existe nenhuma classe TaskQueued. O primeiro frame observável de uma tarefa é TaskDispatched.
Eventos de pipeline
Secção intitulada “Eventos de pipeline”| Evento | Payload | Emitido quando |
|---|---|---|
PipelineRunCompleted |
workspaceId, pipelineRunId, templateId |
A execução termina |
PipelineRunCancelled |
workspaceId, pipelineRunId, templateId |
A execução é cancelada (pelo utilizador ou pelo sistema) — uma paragem deliberada, não um erro |
PipelineRunFailed |
workspaceId, pipelineRunId, templateId, errorMessage |
A execução falha |
Só são publicadas as transições terminais. Não existe nenhum PipelineRunStarted nem qualquer evento por passo: o progresso de um passo chega aos clientes através das próprias linhas da execução e do fluxo do ciclo de vida de tarefas do agente que esta despachou, e não através do barramento.
Eventos de observabilidade
Secção intitulada “Eventos de observabilidade”| Evento | Payload | Emitido quando |
|---|---|---|
ActivityLogged |
id, actorType, actorId?, action, entityType, entityId?, details?, workspaceId?, runId? |
É criada uma entrada no registo de auditoria |
BudgetThresholdCrossed |
scopeType, scopeId, spentCents, budgetCents, isHardStop |
O gasto ultrapassa o limite |
Eventos de identidade e associação
Secção intitulada “Eventos de identidade e associação”| Evento | Payload | Emitido quando |
|---|---|---|
WorkspaceMemberAdded |
workspaceId, userId, role |
Um utilizador entra num espaço de trabalho (resgate de convite ou adição por um administrador) |
WorkspaceMemberRemoved |
workspaceId, userId |
Um membro é removido — as sessões ativas desse utilizador com âmbito no espaço de trabalho têm de voltar a verificar o acesso de imediato |
WorkspaceMemberRoleChanged |
workspaceId, userId, role |
O papel de um membro é alterado |
A associação é toda a superfície de identidade no barramento. A criação de utilizadores e a revogação de dispositivos acontecem, mas não são publicadas como eventos de domínio — a revogação é imposta diretamente contra as sessões ativas, em vez de ser encaminhada pelo barramento.
Eventos de calendário e de reuniões
Secção intitulada “Eventos de calendário e de reuniões”| Evento | Payload | Emitido quando |
|---|---|---|
CalendarAuthExpired |
workspaceId, accountEmail |
O token de atualização OAuth de uma conta ligada fica permanentemente inválido (publicado uma vez por episódio de desligamento) |
MeetingStartingSoon |
workspaceId, eventId, title, startTime, meetingUrl? |
Um evento de calendário vai começar dentro da janela de antecedência configurada |
MeetingRecordingStopped |
workspaceId, meetingId, title, userNotes, transcript, summaryInstructions? |
Uma gravação de reunião termina e desencadeia o pipeline incorporado meeting_summary |
Eventos de rigs
Secção intitulada “Eventos de rigs”| Evento | Payload | Emitido quando |
|---|---|---|
RigClosedEvent |
workspaceId, rigId, reason |
Um rig desapareceu, com o respetivo motivo de fecho; os consumidores só reagem a backendFailure — qualquer outro motivo é pedido ou é acompanhado por um RigReaped |
RigControlChanged |
workspaceId, rigId, controller? |
Uma pessoa assumiu o controlo exclusivo de um rig ou devolveu-o (controller nulo ao libertar — o evento não regista quem largou o controlo) |
RigReaped |
workspaceId, rigId, reason, agentId? |
O sistema recuperou uma máquina: inatividade, TTL ou despejo por pressão de memória (comunicado como idleTimeout), com o agente que a conduzia |
A abertura de um rig não é um evento. O arranque conclui-se e a linha do rig passa a pronto; nada é publicado, por isso não há nenhum RigOpened a que subscrever.
Subdomínios sem quaisquer eventos
Secção intitulada “Subdomínios sem quaisquer eventos”Vários subsistemas que, vistos de fora, parecem orientados a eventos não o são. Estão listados aqui porque a sua ausência é uma decisão de conceção que vale a pena conhecer antes de procurar um hook:
| Subdomínio | Como o estado se altera em vez disso |
|---|---|
| Memória | record_memory_fact e afins escrevem e devolvem; a recordação lê. Nada publica sobre um facto ou conflito |
| Orquestração | A proposta, a sua aprovação e a execução de pipeline gerada são linhas e chamadas RPC |
| Documentos de plano | submit_plan escreve uma revisão; a aprovação materializa uma orquestração. Nenhum dos dois é anunciado |
| Artefactos | publish_artifact / revise_artifact escrevem revisões diretamente |
| Aprovações | A escalada é tratada dentro do serviço de fluxo de aprovação |
| Sincronização de tickets | O coordenador de fornecedores subscreve os eventos de tickets acima; os webhooks de entrada são aplicados diretamente |
| Atualização do calendário | Uma sincronização faz upsert dos eventos e o cliente volta a ler; não há nenhuma difusão de atualização |
| Merges de worktrees | Registados em worktree_merge_log e lidos a partir daí |
Subscritores
Secção intitulada “Subscritores”Todos os subscritores abaixo são construídos dentro do runCcServer (ou de um serviço que este constrói). Não existe nenhum subscritor do lado do cliente.
| Subscritor | Eventos que consome |
|---|---|
WorkspaceSeeder |
WorkspaceCreated |
| Sementeira inicial das suites de avaliação (em linha) | WorkspaceCreated |
| Aprovisionamento do espaço de chat (em linha) | SpaceCreated |
PipelineTriggerDispatcher |
Todos os DomainEvent; cada disparador ativado filtra pelo seu próprio tipo |
SubPipelineResumeListener |
PipelineRunCompleted, PipelineRunFailed |
PipelineCostRollupListener |
AgentRunCompleted |
BudgetEvaluationListener |
AgentRunCompleted |
MeetingSummaryReconciler |
PipelineRunCompleted, PipelineRunFailed, PipelineRunCancelled (apenas execuções de meeting_summary) |
PipelineStepResumeListener |
AgentRunCompleted |
AgentRunTaskCompleter |
AgentRunCompleted |
GoalSupervisor |
AgentRunCompleted |
CheckerDispatchListener |
AgentRunCompleted |
TeamRoutingService |
AgentRunCompleted, TicketAssigned |
OrchestrationRunListener |
PipelineRunCancelled, PipelineRunFailed |
MultiVendorTicketSyncCoordinator |
TicketCreated, TicketAssigned, TicketReassigned, TicketStatusChanged, TicketDetailsUpdated |
WorktreeGcListener |
TicketCompleted, TicketCancelled, SpaceDeleted, PrMerged, PullRequestStatusChanged |
ChatBridgeService |
MessageReceived, SpaceProvisioningChanged |
ActivityLogPersister |
ActivityLogged |
RigEventListener |
RigControlChanged, RigReaped, RigClosedEvent — injeta um aviso em linguagem simples no canal de orientação do agente que conduz |
StaleReviewWatcher |
PrHeadChanged — publica ReviewBecameStale apenas quando existe uma revisão concluída para o commit substituído |
GitHubPrConversationBridge |
AgentRunCompleted — respostas de saída em conversas de PR |
NotificationFeedRecorder |
Os vinte e dois eventos da classe das notificações abaixo |
RemoteEventForwarder |
Esses vinte e dois, mais TicketReassigned e todos os TaskLifecycleEvent. Os três eventos de associação apenas invalidam a respetiva cache de associação por espaço de trabalho; não são reencaminhados como frames |
LocalRpcServer / RemoteRelayHost |
WorkspaceMemberRemoved — elimina as subscrições de espaços de trabalho associadas a esse utilizador; o socket mantém-se aberto |
O TicketAssigned é um sinal de auditoria e de notificação e um disparador de pipeline. A atribuição a um agente regista a propriedade e não inicia nenhuma execução — não existe nenhum despachante de tickets. A atribuição a uma equipa (assignedTeamId definido) é a exceção: o TeamRoutingService despacha o líder da equipa.
O TicketRemoteSyncHandler está declarado mas nunca é construído em código de produção, por isso hoje não subscreve nada. O NotificationEventMapper do lado do cliente só é construído no seu próprio teste — é o vestígio do percurso em processo de antes da separação entre cliente e servidor. Os avisos em direto vêm dos frames de protocolo abaixo.
Mapeamento de notificações
Secção intitulada “Mapeamento de notificações”No servidor, o NotificationFeedRecorder e o RemoteEventForwarder transformam estes eventos em frames de protocolo notifications/* (notification_wire.dart). No cliente, RpcNotificationMapper → mapNotificationFrame transforma um frame num AppNotification.
Existem exatamente dezassete categorias de notificação. Vários eventos correspondem a mais do que um método de transmissão, porque o frame transporta a transição (falhou versus recuperou) enquanto a categoria transporta a pergunta («o que está a CI a fazer?»).
| Categoria | Evento de origem | Método de transmissão |
|---|---|---|
newMessage |
MessageReceived |
notifications/message_received (menciona-o) |
agentRunCompleted |
MessageReceived |
notifications/message_received (is_agent_message, sem o mencionar) |
agentAwaitingInput |
AgentAwaitingInput |
notifications/agent_awaiting_input |
ticketAssigned |
TicketAssigned |
notifications/ticket_assigned |
ticketStatusChanged |
TicketStatusChanged |
notifications/ticket_status_changed |
pullRequestPublished |
PullRequestPublished |
notifications/pr_published |
prMerged |
PrMerged |
notifications/pr_merged |
prMerged |
ExternalPrMerged |
notifications/external_pr_merged |
prMentioned |
PrMentioned |
notifications/pr_mentioned |
prMentioned |
PrCommentMentioned |
notifications/pr_comment_mentioned |
reviewRequested |
PrReviewRequested |
notifications/pr_review_requested |
reviewStale |
ReviewBecameStale |
notifications/review_stale |
prMergeReadiness |
PrMergeReadinessChanged |
notifications/pr_ready_to_merge / pr_merge_blocked |
prReviewDecision |
PrReviewDecisionChanged |
notifications/pr_approved / pr_changes_requested / pr_review_dismissed |
prChecksStatus |
PrChecksStatusChanged |
notifications/pr_checks_failed / pr_checks_recovered |
prThreadActivity |
PrThreadReplied |
notifications/pr_thread_replied |
prThreadActivity |
PrThreadResolved |
notifications/pr_thread_resolved |
meetingStartsSoon |
MeetingStartingSoon |
notifications/meeting_starting_soon |
calendarAuthExpired |
CalendarAuthExpired |
notifications/calendar_auth_expired |
rigStatusChanged |
RigControlChanged |
notifications/rig_control_changed |
rigStatusChanged |
RigReaped |
notifications/rig_reaped |
rigStatusChanged |
RigClosedEvent |
notifications/rig_closed |
O frame de assunção do controlo do rig não é entregue de volta a quem pegou no
volante, uma libertação não é filtrada (o evento não regista quem largou o
controlo) e só um fecho por backendFailure produz um frame rig_closed — os
outros motivos ou são pedidos ou já estão cobertos pelo frame de recuperação.
AgentAwaitingInput é publicado pelo AgentAwaitingInputPublisher, que vigia
os registos de aprovações e de bloqueios de credenciais à procura de novas
entradas e é informado de cada pergunta ask_user pelo AgentQuestionService.
Uma espera corresponde a um evento; nada é publicado quando a espera termina.
As cinco categorias de PRs próprias transportam forUserId, por isso chegam ao autor em vez de chegarem a todos no espaço de trabalho.
O RemoteEventForwarder reencaminha ainda o TicketReassigned como notifications/ticket_reassigned e todos os TaskLifecycleEvent como notifications/task_<phase>. Nenhum deles corresponde a uma categoria de notificação — alimentam a interface em direto, não o centro de notificações. Os eventos de associação não são reencaminhados como frames: invalidam a cache do reencaminhador e os anfitriões de sessão eliminam as subscrições de espaços de trabalho do membro removido.