Eventos de dominio
Bus de eventos
Sección titulada «Bus de eventos»El DomainEventBus es un bus de publicación/suscripción con difusión y en proceso. Quien publica llama a publish(event); quien se suscribe consume un flujo tipado on<T>(). Todos los eventos implementan DomainEvent y llevan occurredAt.
El bus se ejecuta dentro de cc_server. Tanto quien publica como quien está suscrito en vivo residen ahí. Ningún cliente ve este bus: el servidor reenvía un subconjunto seleccionado como tramas JSON-RPC notifications/*, filtradas por suscriptor según la pertenencia al espacio de trabajo.
| Medida | Recuento |
|---|---|
| Clases de evento concretas | 54 |
Archivos de origen (packages/cc_domain/lib/core/domain/events/) |
14 (más domain_event_bus.dart) |
| Bases abstractas que nunca se publican directamente | 1 (TaskLifecycleEvent, una jerarquía sellada) |
Todos los eventos del catálogo de abajo viven en ese único directorio; no hay ningún evento de dominio declarado en otro sitio.
Eventos de espacios de trabajo, agentes y repos
Sección titulada «Eventos de espacios de trabajo, agentes y repos»| Evento | Carga útil | Se activa cuando |
|---|---|---|
WorkspaceCreated |
workspaceId |
Se crea un espacio de trabajo |
AgentRunCompleted |
agentId, runId?, workspaceId?, conversationId? |
Un agente termina una ejecución (con éxito o con error) |
AgentAwaitingInput |
workspaceId, kind, summary, spaceId?, conversationId?, agentId?, agentName? |
Un agente empieza a esperar a una persona: una aprobación, una pregunta ask_user o una ejecución detenida por una credencial (kind es approval, question o credential) |
RepoAdded |
repoId, path, workspaceId |
Se registra un repo; activa la indexación de código en segundo plano |
SkillUpdated |
workspaceId, slug, origin, computedHash, scanVerdict? |
Se instala o actualiza una skill, con su veredicto del análisis de la cadena de suministro |
Eventos de PR y revisión
Sección titulada «Eventos de PR y revisión»El primer bloque es el ciclo de vida propio de la PR. El segundo es el seguimiento de PRs propias: señales sobre una PR que tú abriste, cada una con forUserId para que el sondeo pueda dirigirlas a la persona a la que conciernen en lugar de difundirlas.
| Evento | Carga útil | Se activa cuando |
|---|---|---|
PullRequestPublished |
prId, workspaceId, repoOwner, repoName |
Un agente abre una PR |
PullRequestStatusChanged |
status, workspaceId, prId?, repoFullName?, prNumber? |
La PR se fusiona, se cierra, se abre, se reabre o se aprueba |
PrMerged |
prId, workspaceId, agentId |
La PR se fusiona: una señal acotada solo a fusiones que se conserva para análisis y notificaciones |
PrReviewRequested |
workspaceId, repoOwner, repoName, prNumber, prTitle |
GitHub solicita la revisión del usuario del servidor (sondeo de notificaciones, review_requested) |
PrMentioned |
workspaceId, repoOwner, repoName, prNumber, prTitle |
Se menciona al usuario del servidor en una PR (sondeo de notificaciones, mention) |
ExternalPrMerged |
workspaceId, repoOwner, repoName, prNumber, prTitle, mergedByLogin? |
Se fusiona una PR de seguimiento externo (no de un agente), detectada por el sondeo de notificaciones de GitHub; se corresponde con la categoría de notificación prMerged |
ExternalPrDetected |
repoOwner, repoName, prNumber, prTitle, author, workspaceId? |
PR de origen no agente encontrada mediante sondeo: solo para pipelines (alimenta external_pr_welcome), no genera ninguna notificación |
PrHeadChanged |
repoOwner, repoName, prNumber, prTitle, previousHeadSha, headSha, workspaceId |
El SHA head de la PR cambió: el autor hizo push |
ReviewBecameStale |
workspaceId, spaceId, repoOwner, repoName, prNumber, prTitle, reviewedHeadSha, headSha |
Una revisión con IA finalizada ya no describe la PR que revisó. Solo se activa cuando existe una revisión del commit sustituido, así que sigue siendo una señal y no un aviso por push |
El seguimiento de PRs propias
Sección titulada «El seguimiento de PRs propias»Los seis llevan workspaceId, repoOwner, repoName, prNumber, prTitle y un forUserId opcional.
| Evento | Carga útil adicional | Se activa cuando |
|---|---|---|
PrMergeReadinessChanged |
ready, reason |
Una PR que escribiste pasó a ser fusionable, o dejó de serlo |
PrReviewDecisionChanged |
decision, reviewersRemaining, approverLogin? |
Un revisor aprobó, solicitó cambios o se anuló una aprobación suya |
PrChecksStatusChanged |
failing, failingCheckName?, failingCheckUrl? |
La CI pasó a estar en rojo, o se recuperó |
PrCommentMentioned |
commentId, authorLogin, bodyPreview, isReviewComment, threadId?, path?, line? |
Alguien te mencionó en un comentario de una PR o de una revisión |
PrThreadReplied |
commentId, authorLogin, bodyPreview, threadId?, path?, line? |
Alguien respondió en un hilo de revisión en el que estás |
PrThreadResolved |
threadId, commentId?, path?, line? |
Alguien resolvió un hilo de revisión en el que estás |
Eventos de mensajería
Sección titulada «Eventos de mensajería»| Evento | Carga útil | Se activa cuando |
|---|---|---|
MessageReceived |
spaceId, messageId, senderName, contentPreview, isAgentMessage, workspaceId?, mentions, requestedByUserId?, senderUserId? |
Llega un mensaje nuevo |
SpaceCreated |
spaceId, workspaceId |
Se crea un espacio |
SpaceDeleted |
spaceId, workspaceId |
Se elimina un espacio |
SpaceProvisioningChanged |
workspaceId, spaceId, status, step? |
El espacio de trabajo del espacio entra en aprovisionamiento, alcanza un paso (clonar un repo, hacer checkout de una PR, configurar un agente) o pasa a listo o fallido: el puente de chat lo narra en su tarjeta de tarea |
Eventos de tickets
Sección titulada «Eventos de tickets»| Evento | Carga útil | Se activa cuando |
|---|---|---|
TicketCreated |
ticketId, workspaceId |
Se crea un ticket |
TicketAssigned |
ticketId, ticketTitle, ticketBody?, ticketUrl?, assignedAgentId?, assigneeType, assignedTeamId?, workspaceId |
Ticket asignado a un principal o a un equipo |
TicketCompleted |
ticketId |
El trabajo finaliza (terminal) |
TicketFailed |
ticketId, errorMessage |
El agente falla (terminal) |
TicketCancelled |
ticketId |
Ticket cancelado (terminal) |
TicketStatusChanged |
ticketId, workspaceId, from, to |
Cualquier cambio de estado |
TicketReassigned |
ticketId, workspaceId, fromAgentId?, toAgentId? |
Ticket reasignado |
TicketDetailsUpdated |
ticketId, workspaceId |
Cambian el título, la descripción o la prioridad |
Eventos del ciclo de vida de las tareas
Sección titulada «Eventos del ciclo de vida de las tareas»El flujo unificado del ciclo de vida de las tareas: una ejecución de agente despachada, identificada por el id de su registro de ejecución. Todos los eventos llevan taskId, un seq monotónico por tarea, workspaceId?, agentId? y una phase; las filas de abajo solo enumeran los campos adicionales.
| Evento | Carga útil | Se activa cuando |
|---|---|---|
TaskDispatched |
— | La tarea se entrega a un backend para su ejecución |
TaskRunning |
— | La tarea empezó a producir salida |
TaskWaitingLocalDirectory |
lockedPath, holderTaskId? |
La tarea queda en espera: otra tarea posee el bloqueo de ruta en disco que necesita |
TaskProgress |
note? |
Punto de control de progreso a mitad de ejecución |
TaskMessage |
messageType, content |
Mensaje transmitido en flujo (text/thinking/tool_use/tool_result/error); se informa bajo la fase progress |
TaskCompleted |
summary? |
La tarea terminó correctamente |
TaskFailed |
errorMessage |
La tarea terminó con un error |
TaskCancelled |
— | La tarea se canceló antes de completarse |
La enumeración TaskPhase también tiene un miembro queued, pero nada publica un evento en cola: no existe ninguna clase TaskQueued. La primera trama observable de una tarea es TaskDispatched.
Eventos de pipeline
Sección titulada «Eventos de pipeline»| Evento | Carga útil | Se activa cuando |
|---|---|---|
PipelineRunCompleted |
workspaceId, pipelineRunId, templateId |
La ejecución finaliza |
PipelineRunCancelled |
workspaceId, pipelineRunId, templateId |
Ejecución cancelada (por el usuario o por el sistema): una detención deliberada, no un error |
PipelineRunFailed |
workspaceId, pipelineRunId, templateId, errorMessage |
La ejecución falla |
Solo se publican las transiciones terminales. No existe ningún PipelineRunStarted ni ningún evento por paso: el progreso de un paso llega a los clientes a través de las propias filas de la ejecución y del flujo del ciclo de vida de la tarea del agente que haya despachado, no a través del bus.
Eventos de observabilidad
Sección titulada «Eventos de observabilidad»| Evento | Carga útil | Se activa cuando |
|---|---|---|
ActivityLogged |
id, actorType, actorId?, action, entityType, entityId?, details?, workspaceId?, runId? |
Se crea una entrada del registro de auditoría |
BudgetThresholdCrossed |
scopeType, scopeId, spentCents, budgetCents, isHardStop |
El gasto supera el umbral |
Eventos de identidad y pertenencia
Sección titulada «Eventos de identidad y pertenencia»| Evento | Carga útil | Se activa cuando |
|---|---|---|
WorkspaceMemberAdded |
workspaceId, userId, role |
Un usuario se une a un espacio de trabajo (canje de una invitación o alta de un administrador) |
WorkspaceMemberRemoved |
workspaceId, userId |
Se elimina a un miembro: las sesiones activas de ese usuario con ámbito del espacio de trabajo deben volver a comprobar el acceso de inmediato |
WorkspaceMemberRoleChanged |
workspaceId, userId, role |
Cambia el rol de un miembro |
La pertenencia es toda la superficie de identidad que hay en el bus. La creación de usuarios y la revocación de dispositivos ocurren, pero no se publican como eventos de dominio: la revocación se aplica directamente contra las sesiones activas en lugar de canalizarse por el bus.
Eventos de calendario y reuniones
Sección titulada «Eventos de calendario y reuniones»| Evento | Carga útil | Se activa cuando |
|---|---|---|
CalendarAuthExpired |
workspaceId, accountEmail |
El token de refresco de OAuth de una cuenta conectada queda permanentemente inválido (se publica una vez por cada episodio de desconexión) |
MeetingStartingSoon |
workspaceId, eventId, title, startTime, meetingUrl? |
Un evento de calendario está a punto de empezar dentro de la ventana de antelación configurada |
MeetingRecordingStopped |
workspaceId, meetingId, title, userNotes, transcript, summaryInstructions? |
Una grabación de reunión finaliza y activa el pipeline integrado meeting_summary |
Eventos de rig
Sección titulada «Eventos de rig»| Evento | Carga útil | Se activa cuando |
|---|---|---|
RigClosedEvent |
workspaceId, rigId, reason |
Un rig desapareció, con su motivo de cierre; los consumidores solo reaccionan a backendFailure: cualquier otro motivo es solicitado o va acompañado de un RigReaped |
RigControlChanged |
workspaceId, rigId, controller? |
Una persona tomó el control exclusivo de un rig o lo devolvió (controller es nulo al liberarlo: el evento no registra quién lo soltó) |
RigReaped |
workspaceId, rigId, reason, agentId? |
El sistema reclamó una máquina: por inactividad, por TTL o por desalojo debido a presión de memoria (notificado como idleTimeout), con el agente que la estaba dirigiendo |
La apertura de un rig no es un evento. El arranque se completa y la fila del rig pasa a listo; no se publica nada, así que no hay ningún RigOpened al que suscribirse.
Subdominios sin ningún evento
Sección titulada «Subdominios sin ningún evento»Varios subsistemas que desde fuera parecen basados en eventos no lo son. Se enumeran aquí porque su ausencia es una decisión de diseño que conviene conocer antes de ponerse a buscar un enganche:
| Subdominio | Cómo cambia el estado en su lugar |
|---|---|
| Memoria | record_memory_fact y sus compañeras escriben y devuelven; la recuperación lee. Nada publica al registrar un hecho o un conflicto |
| Orquestación | La propuesta, su aprobación y la ejecución de pipeline generada son filas y llamadas RPC |
| Documentos de plan | submit_plan escribe una revisión; la aprobación materializa una orquestación. Ninguna de las dos se anuncia |
| Artefactos | publish_artifact / revise_artifact escriben revisiones directamente |
| Aprobaciones | La escalada se gestiona dentro del servicio de flujo de aprobación |
| Sincronización de tickets | El coordinador de proveedores se suscribe a los eventos de tickets de arriba; los webhooks entrantes se aplican directamente |
| Actualización del calendario | Una sincronización hace upsert de los eventos y el cliente vuelve a leer; no hay ninguna difusión de actualización |
| Fusiones de worktree | Se registran en worktree_merge_log y se leen desde ahí |
Suscriptores
Sección titulada «Suscriptores»Todos los suscriptores de abajo se construyen dentro de runCcServer (o de un servicio que este crea). No hay ningún suscriptor del lado del cliente.
| Suscriptor | Eventos que consume |
|---|---|
WorkspaceSeeder |
WorkspaceCreated |
| Siembra de la suite de evaluación inicial (en línea) | WorkspaceCreated |
| Aprovisionamiento de espacios de chat (en línea) | SpaceCreated |
PipelineTriggerDispatcher |
Todos los DomainEvent; cada disparador habilitado filtra por su propio tipo |
SubPipelineResumeListener |
PipelineRunCompleted, PipelineRunFailed |
PipelineCostRollupListener |
AgentRunCompleted |
BudgetEvaluationListener |
AgentRunCompleted |
MeetingSummaryReconciler |
PipelineRunCompleted, PipelineRunFailed, PipelineRunCancelled (solo las ejecuciones 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: inyecta un aviso en lenguaje claro en el canal de dirección del agente que la está dirigiendo |
StaleReviewWatcher |
PrHeadChanged: publica ReviewBecameStale solo cuando existe una revisión finalizada del commit sustituido |
GitHubPrConversationBridge |
AgentRunCompleted: respuestas salientes de conversaciones de PR |
NotificationFeedRecorder |
Los veintidós eventos de clase de notificación de abajo |
RemoteEventForwarder |
Esos veintidós más TicketReassigned y todos los TaskLifecycleEvent. Los tres eventos de pertenencia solo invalidan su caché de pertenencia por espacio de trabajo; no se reenvían como tramas |
LocalRpcServer / RemoteRelayHost |
WorkspaceMemberRemoved: descarta las suscripciones a espacios de trabajo que ese usuario tenía enganchadas; el socket permanece abierto |
TicketAssigned es una señal de auditoría y de notificación, y un disparador de pipeline. La asignación a un agente registra la titularidad y no inicia ninguna ejecución: no hay ningún despachador de tickets. La asignación a un equipo (assignedTeamId definido) es la excepción: TeamRoutingService despacha al líder del equipo.
TicketRemoteSyncHandler está declarado, pero nunca se construye en el código de producción, así que hoy no se suscribe a nada. El NotificationEventMapper del lado del cliente solo se construye en su propio test: es el vestigio de la ruta en proceso anterior a la separación entre cliente y servidor. Los toasts en vivo proceden de las tramas de abajo.
Mapeo de notificaciones
Sección titulada «Mapeo de notificaciones»En el servidor, NotificationFeedRecorder y RemoteEventForwarder convierten estos eventos en tramas notifications/* (notification_wire.dart). En el cliente, RpcNotificationMapper → mapNotificationFrame convierte una trama en una AppNotification.
Hay exactamente diecisiete categorías de notificación. Varios eventos se corresponden con más de un método de trama, porque la trama transporta el cambio (fallo frente a recuperación) mientras que la categoría transporta la pregunta («¿qué está haciendo la CI?»).
| Categoría | Evento de origen | Método de trama |
|---|---|---|
newMessage |
MessageReceived |
notifications/message_received (te menciona) |
agentRunCompleted |
MessageReceived |
notifications/message_received (is_agent_message, sin mencionarte) |
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 |
La trama de toma de control de un rig no se entrega de vuelta a quien tomó los mandos, una
liberación no se filtra (el evento no registra quién lo soltó) y solo un
cierre por backendFailure produce una trama rig_closed: los demás motivos
o son solicitados o ya los cubre la trama de recolección.
AgentAwaitingInput lo publica AgentAwaitingInputPublisher, que vigila los registros de aprobaciones y de bloqueos de credenciales en busca de entradas nuevas y al que AgentQuestionService avisa de cada pregunta ask_user. Una espera es un evento; cuando la espera termina, no se publica nada.
Las cinco categorías de PR propias llevan forUserId, así que llegan al autor y no a todos los miembros del espacio de trabajo.
RemoteEventForwarder reenvía además TicketReassigned como notifications/ticket_reassigned y todos los TaskLifecycleEvent como notifications/task_<phase>. Ninguno de los dos se corresponde con una categoría de notificación: alimentan la interfaz en vivo, no el centro de notificaciones. Los eventos de pertenencia no se reenvían como tramas: invalidan la caché del reenviador y los hosts de sesión descartan las suscripciones a espacios de trabajo del miembro eliminado.