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 | Cantidad |
|---|---|
| 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 eventos de dominio declarados en ningún otro lugar.
Eventos de espacios de trabajo, agentes y repos
Sección titulada «Eventos de espacios de trabajo, agentes y repos»| Evento | Carga útil | Se emite 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 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 a todos.
| Evento | Carga útil | Se emite 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 de fusión, que se conserva para analíticas 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 (que no es de un agente), detectada por el sondeo de notificaciones de GitHub; se asigna a la categoría de notificación prMerged |
ExternalPrDetected |
repoOwner, repoName, prNumber, prTitle, author, workspaceId? |
PR que no es de un agente, encontrada mediante sondeo: solo para pipelines (impulsa external_pr_welcome), no genera ninguna notificación |
PrHeadChanged |
repoOwner, repoName, prNumber, prTitle, previousHeadSha, headSha, workspaceId |
El SHA de cabeza de la PR cambió: el autor hizo push |
ReviewBecameStale |
workspaceId, spaceId, repoOwner, repoName, prNumber, prTitle, reviewedHeadSha, headSha |
Una revisión de IA terminada ya no describe la PR que revisó. Solo se emite cuando existe una revisión para el commit reemplazado, así que sigue siendo una señal y no un aviso por cada push |
El seguimiento de PRs propias
Sección titulada «El seguimiento de PRs propias»Las seis llevan workspaceId, repoOwner, repoName, prNumber, prTitle y un forUserId opcional.
| Evento | Carga útil adicional | Se emite cuando |
|---|---|---|
PrMergeReadinessChanged |
ready, reason |
Una PR de tu autoría pasó a ser fusionable o dejó de serlo |
PrReviewDecisionChanged |
decision, reviewersRemaining, approverLogin? |
Un revisor aprobó, solicitó cambios o se descartó una aprobación |
PrChecksStatusChanged |
failing, failingCheckName?, failingCheckUrl? |
La CI se puso en rojo o se recuperó |
PrCommentMentioned |
commentId, authorLogin, bodyPreview, isReviewComment, threadId?, path?, line? |
Alguien te mencionó en un comentario de PR o de revisión |
PrThreadReplied |
commentId, authorLogin, bodyPreview, threadId?, path?, line? |
Alguien respondió en un hilo de revisión en el que participas |
PrThreadResolved |
threadId, commentId?, path?, line? |
Alguien resolvió un hilo de revisión en el que participas |
Eventos de mensajería
Sección titulada «Eventos de mensajería»| Evento | Carga útil | Se emite 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/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 emite 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 termina (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 tareas
Sección titulada «Eventos del ciclo de vida de 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. Cada evento lleva taskId, un seq monótono por tarea, workspaceId?, agentId? y una phase; las filas de abajo enumeran solo los campos adicionales.
| Evento | Carga útil | Se emite cuando |
|---|---|---|
TaskDispatched |
— | La tarea se entrega a un backend para su ejecución |
TaskRunning |
— | La tarea empezó a producir salida |
TaskWaitingLocalDirectory |
lockedPath, holderTaskId? |
Tarea en espera: otra tarea posee el bloqueo de la ruta en disco que necesita |
TaskProgress |
note? |
Punto de control de progreso a mitad de la ejecución |
TaskMessage |
messageType, content |
Mensaje transmitido (text/thinking/tool_use/tool_result/error); se reporta 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 de cola: no existe la clase TaskQueued. La primera trama observable de una tarea es TaskDispatched.
Eventos de pipelines
Sección titulada «Eventos de pipelines»| Evento | Carga útil | Se emite cuando |
|---|---|---|
PipelineRunCompleted |
workspaceId, pipelineRunId, templateId |
La ejecución termina |
PipelineRunCancelled |
workspaceId, pipelineRunId, templateId |
La ejecución se cancela (por el usuario o 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 PipelineRunStarted ni ningún evento por paso: el progreso de un paso llega a los clientes a través de las filas de la propia ejecución y del flujo del ciclo de vida de tareas del agente que haya despachado, no a través del bus.
Eventos de observabilidad
Sección titulada «Eventos de observabilidad»| Evento | Carga útil | Se emite 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 emite cuando |
|---|---|---|
WorkspaceMemberAdded |
workspaceId, userId, role |
Un usuario se une a un espacio de trabajo (canje de invitación o alta por un administrador) |
WorkspaceMemberRemoved |
workspaceId, userId |
Se elimina a un miembro: las sesiones activas de ese usuario en el 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 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 emite 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á por comenzar dentro de la ventana de anticipación configurada |
MeetingRecordingStopped |
workspaceId, meetingId, title, userNotes, transcript, summaryInstructions? |
Una grabación de reunión termina y activa el pipeline integrado meeting_summary |
Eventos de rigs
Sección titulada «Eventos de rigs»| Evento | Carga útil | Se emite cuando |
|---|---|---|
RigClosedEvent |
workspaceId, rigId, reason |
Un rig desapareció, con su motivo de cierre; los consumidores solo actúan ante backendFailure: cualquier otro motivo es solicitado o viene 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 recuperó una máquina: inactividad, TTL o expulsión por presión de memoria (reportado como idleTimeout), junto con el agente que la dirigía |
La apertura de un rig no es un evento. El arranque termina 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 están. Se enumeran aquí porque su ausencia es una decisión de diseño que conviene conocer antes de salir a buscar un enganche:
| Subdominio | Cómo se mueve el estado en su lugar |
|---|---|
| Memoria | record_memory_fact y compañía escriben y devuelven; la recuperación lee. Nada se publica ante 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 maneja dentro del servicio de flujo de trabajo de aprobaciones |
| 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 worktrees | 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 |
| Sembrado de la suite de evaluación inicial (en línea) | WorkspaceCreated |
| Aprovisionamiento del espacio 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 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 sencillo en el canal de dirección del agente que la conduce |
StaleReviewWatcher |
PrHeadChanged: publica ReviewBecameStale solo cuando existe una revisión terminada para el commit reemplazado |
GitHubPrConversationBridge |
AgentRunCompleted: respuestas salientes en conversaciones de PR |
NotificationFeedRecorder |
Los veintidós eventos de clase de notificación que aparecen 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 conectadas; el socket permanece abierto |
TicketAssigned es una señal de auditoría y notificación, y un disparador de pipeline. La asignación a un agente registra la propiedad 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 propia prueba: es el vestigio de la ruta en proceso anterior a la separación entre cliente y servidor. Los avisos en vivo provienen de las tramas que se describen abajo.
Asignación de notificaciones
Sección titulada «Asignación 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 asignan a más de un método de la trama, porque la trama lleva el matiz (falló o se recuperó) mientras que la categoría lleva la pregunta («¿qué está haciendo la CI?»).
| Categoría | Evento de origen | Método de la 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ó el control, 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 están cubiertos por la trama de recuperació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 PRs propias llevan forUserId, así que llegan al autor en lugar de a todos los del espacio de trabajo.
RemoteEventForwarder además reenvía TicketReassigned como notifications/ticket_reassigned y todos los TaskLifecycleEvent como notifications/task_<phase>. Ninguno se asigna a una categoría de notificación: impulsan 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.