Zdarzenia domenowe
Szyna zdarzeń
Dział zatytułowany „Szyna zdarzeń”DomainEventBus to działająca w obrębie procesu szyna publikowania i subskrypcji z rozgłaszaniem. Wydawcy wywołują publish(event); subskrybenci konsumują typowany strumień on<T>(). Każde zdarzenie implementuje DomainEvent i niesie occurredAt.
Szyna działa wewnątrz cc_server. Każdy wydawca i każdy żywy subskrybent mieszka tam. Żaden klient nie widzi tej szyny — serwer przekazuje wyselekcjonowany podzbiór jako ramki JSON-RPC notifications/*, filtrowany dla każdego subskrybenta według członkostwa w obszarze roboczym.
| Miara | Liczba |
|---|---|
| Konkretne klasy zdarzeń | 54 |
Pliki źródłowe (packages/cc_domain/lib/core/domain/events/) |
14 (plus domain_event_bus.dart) |
| Abstrakcyjne klasy bazowe, które nigdy nie są publikowane bezpośrednio | 1 (TaskLifecycleEvent, zapieczętowana hierarchia) |
Każde zdarzenie z poniższego katalogu mieszka w tym jednym katalogu; żadne zdarzenia domenowe nie są zadeklarowane gdzie indziej.
Zdarzenia obszaru roboczego, agenta i repozytorium
Dział zatytułowany „Zdarzenia obszaru roboczego, agenta i repozytorium”| Zdarzenie | Ładunek | Wywoływane, gdy |
|---|---|---|
WorkspaceCreated |
workspaceId |
Tworzony jest obszar roboczy |
AgentRunCompleted |
agentId, runId?, workspaceId?, conversationId? |
Agent kończy uruchomienie (sukces lub porażka) |
AgentAwaitingInput |
workspaceId, kind, summary, spaceId?, conversationId?, agentId?, agentName? |
Agent zaczyna czekać na człowieka: na zgodę, na odpowiedź na pytanie ask_user albo z uruchomieniem wstrzymanym na danych logowania (kind to approval, question lub credential) |
RepoAdded |
repoId, path, workspaceId |
Rejestrowane jest repozytorium; uruchamia indeksowanie kodu w tle |
SkillUpdated |
workspaceId, slug, origin, computedHash, scanVerdict? |
Skill jest instalowany lub aktualizowany, niosąc werdykt skanowania łańcucha dostaw |
Zdarzenia pull requestów i recenzji
Dział zatytułowany „Zdarzenia pull requestów i recenzji”Pierwszy blok to własny cykl życia PR-a. Drugi to obserwacja autorskich PR-ów: sygnały o PR-ze, który Ty otworzyłeś, z których każdy niesie forUserId, dzięki czemu odpytywanie może kierować je do osoby, której dotyczą, zamiast rozgłaszać je wszystkim.
| Zdarzenie | Ładunek | Wywoływane, gdy |
|---|---|---|
PullRequestPublished |
prId, workspaceId, repoOwner, repoName |
Agent otwiera PR |
PullRequestStatusChanged |
status, workspaceId, prId?, repoFullName?, prNumber? |
PR scalony/zamknięty/otwarty/ponownie otwarty/zatwierdzony |
PrMerged |
prId, workspaceId, agentId |
PR zostaje scalony — wąski sygnał wyłącznie o scaleniu, zachowany na potrzeby analityki i powiadomień |
PrReviewRequested |
workspaceId, repoOwner, repoName, prNumber, prTitle |
GitHub prosi o recenzję od użytkownika serwera (odpytywanie powiadomień, review_requested) |
PrMentioned |
workspaceId, repoOwner, repoName, prNumber, prTitle |
Użytkownik serwera zostaje wspomniany w PR-ze (odpytywanie powiadomień, mention) |
ExternalPrMerged |
workspaceId, repoOwner, repoName, prNumber, prTitle, mergedByLogin? |
Scalono śledzony zewnętrznie (nieagentowy) PR, wykryty przez odpytywanie powiadomień GitHuba; mapuje się na kategorię powiadomień prMerged |
ExternalPrDetected |
repoOwner, repoName, prNumber, prTitle, author, workspaceId? |
Nieagentowy PR znaleziony przez odpytywanie — wyłącznie dla potoków (napędza external_pr_welcome), nie zgłasza powiadomienia |
PrHeadChanged |
repoOwner, repoName, prNumber, prTitle, previousHeadSha, headSha, workspaceId |
Head SHA PR-a się przesunął — autor wypchnął zmiany |
ReviewBecameStale |
workspaceId, spaceId, repoOwner, repoName, prNumber, prTitle, reviewedHeadSha, headSha |
Zakończona recenzja AI nie opisuje już PR-a, który recenzowała. Wywoływane tylko wtedy, gdy istnieje recenzja zastąpionego commita, więc pozostaje sygnałem, a nie pingiem przy każdym pushu |
Obserwacja autorskich PR-ów
Dział zatytułowany „Obserwacja autorskich PR-ów”Wszystkie sześć niosą workspaceId, repoOwner, repoName, prNumber, prTitle oraz opcjonalny forUserId.
| Zdarzenie | Dodatkowy ładunek | Wywoływane, gdy |
|---|---|---|
PrMergeReadinessChanged |
ready, reason |
PR, którego jesteś autorem, stał się możliwy do scalenia albo przestał być |
PrReviewDecisionChanged |
decision, reviewersRemaining, approverLogin? |
Recenzent zatwierdził, zażądał zmian albo jego zatwierdzenie zostało odrzucone |
PrChecksStatusChanged |
failing, failingCheckName?, failingCheckUrl? |
CI się zaczerwieniło albo znów zadziałało |
PrCommentMentioned |
commentId, authorLogin, bodyPreview, isReviewComment, threadId?, path?, line? |
Ktoś wspomniał o Tobie w komentarzu do PR-a albo komentarzu recenzji |
PrThreadReplied |
commentId, authorLogin, bodyPreview, threadId?, path?, line? |
Ktoś odpowiedział w wątku recenzji, w którym uczestniczysz |
PrThreadResolved |
threadId, commentId?, path?, line? |
Ktoś rozwiązał wątek recenzji, w którym uczestniczysz |
Zdarzenia wiadomości
Dział zatytułowany „Zdarzenia wiadomości”| Zdarzenie | Ładunek | Wywoływane, gdy |
|---|---|---|
MessageReceived |
spaceId, messageId, senderName, contentPreview, isAgentMessage, workspaceId?, mentions, requestedByUserId?, senderUserId? |
Przychodzi nowa wiadomość |
SpaceCreated |
spaceId, workspaceId |
Tworzona jest przestrzeń |
SpaceDeleted |
spaceId, workspaceId |
Przestrzeń zostaje usunięta |
SpaceProvisioningChanged |
workspaceId, spaceId, status, step? |
Obszar roboczy przestrzeni wchodzi w provisionowanie, osiąga krok (klonowanie repozytorium, checkout PR-a, konfigurowanie agenta) albo przechodzi w gotowość/awarię — most czatu relacjonuje to na swojej karcie zadania |
Zdarzenia zgłoszeń
Dział zatytułowany „Zdarzenia zgłoszeń”| Zdarzenie | Ładunek | Wywoływane, gdy |
|---|---|---|
TicketCreated |
ticketId, workspaceId |
Zgłoszenie zostaje utworzone |
TicketAssigned |
ticketId, ticketTitle, ticketBody?, ticketUrl?, assignedAgentId?, assigneeType, assignedTeamId?, workspaceId |
Zgłoszenie przypisane do podmiotu lub zespołu |
TicketCompleted |
ticketId |
Praca się kończy (stan terminalny) |
TicketFailed |
ticketId, errorMessage |
Agent zawodzi (stan terminalny) |
TicketCancelled |
ticketId |
Zgłoszenie anulowane (stan terminalny) |
TicketStatusChanged |
ticketId, workspaceId, from, to |
Każda zmiana statusu |
TicketReassigned |
ticketId, workspaceId, fromAgentId?, toAgentId? |
Zgłoszenie przypisane ponownie |
TicketDetailsUpdated |
ticketId, workspaceId |
Zmiana tytułu, opisu lub priorytetu |
Zdarzenia cyklu życia zadania
Dział zatytułowany „Zdarzenia cyklu życia zadania”Ujednolicony strumień cyklu życia zadania — jedno wysłane uruchomienie agenta, kluczowane identyfikatorem dziennika uruchomienia. Każde zdarzenie niesie taskId, monotoniczny dla danego zadania seq, workspaceId?, agentId? oraz phase; poniższe wiersze wymieniają tylko dodatkowe pola.
| Zdarzenie | Ładunek | Wywoływane, gdy |
|---|---|---|
TaskDispatched |
— | Zadanie przekazane do backendu w celu wykonania |
TaskRunning |
— | Zadanie zaczęło produkować wynik |
TaskWaitingLocalDirectory |
lockedPath, holderTaskId? |
Zadanie zaparkowane — inne zadanie jest właścicielem blokady ścieżki na dysku, której potrzebuje |
TaskProgress |
note? |
Punkt kontrolny postępu w trakcie uruchomienia |
TaskMessage |
messageType, content |
Strumieniowana wiadomość (text/thinking/tool_use/tool_result/error); raportowana w fazie progress |
TaskCompleted |
summary? |
Zadanie zakończone pomyślnie |
TaskFailed |
errorMessage |
Zadanie zakończone błędem |
TaskCancelled |
— | Zadanie anulowane przed ukończeniem |
Enum TaskPhase ma także składową queued, ale nic nie publikuje zdarzenia zakolejkowania — nie istnieje klasa TaskQueued. Pierwszą obserwowalną ramką zadania jest TaskDispatched.
Zdarzenia potoków
Dział zatytułowany „Zdarzenia potoków”| Zdarzenie | Ładunek | Wywoływane, gdy |
|---|---|---|
PipelineRunCompleted |
workspaceId, pipelineRunId, templateId |
Uruchomienie się kończy |
PipelineRunCancelled |
workspaceId, pipelineRunId, templateId |
Uruchomienie anulowane (przez użytkownika lub system) — celowe zatrzymanie, a nie błąd |
PipelineRunFailed |
workspaceId, pipelineRunId, templateId, errorMessage |
Uruchomienie zawodzi |
Publikowane są tylko przejścia terminalne. Nie ma PipelineRunStarted ani zdarzenia na krok: postęp kroku dociera do klientów przez własne wiersze uruchomienia i strumień cyklu życia zadania tego agenta, którego krok wysłał, a nie przez szynę.
Zdarzenia obserwowalności
Dział zatytułowany „Zdarzenia obserwowalności”| Zdarzenie | Ładunek | Wywoływane, gdy |
|---|---|---|
ActivityLogged |
id, actorType, actorId?, action, entityType, entityId?, details?, workspaceId?, runId? |
Utworzono wpis dziennika audytu |
BudgetThresholdCrossed |
scopeType, scopeId, spentCents, budgetCents, isHardStop |
Wydatki przekraczają próg |
Zdarzenia tożsamości i członkostwa
Dział zatytułowany „Zdarzenia tożsamości i członkostwa”| Zdarzenie | Ładunek | Wywoływane, gdy |
|---|---|---|
WorkspaceMemberAdded |
workspaceId, userId, role |
Użytkownik dołącza do obszaru roboczego (wykorzystanie zaproszenia albo dodanie przez administratora) |
WorkspaceMemberRemoved |
workspaceId, userId |
Członek zostaje usunięty — aktywne sesje tego użytkownika ograniczone do obszaru roboczego muszą natychmiast ponownie sprawdzić dostęp |
WorkspaceMemberRoleChanged |
workspaceId, userId, role |
Zmienia się rola członka |
Członkostwo to cała powierzchnia tożsamości na szynie. Utworzenie użytkownika i unieważnienie urządzenia mają miejsce, ale nie są publikowane jako zdarzenia domenowe — unieważnienie jest egzekwowane bezpośrednio wobec aktywnych sesji, a nie kierowane przez szynę.
Zdarzenia kalendarza i spotkań
Dział zatytułowany „Zdarzenia kalendarza i spotkań”| Zdarzenie | Ładunek | Wywoływane, gdy |
|---|---|---|
CalendarAuthExpired |
workspaceId, accountEmail |
Token odświeżania OAuth połączonego konta jest trwale nieprawidłowy (publikowane raz na epizod rozłączenia) |
MeetingStartingSoon |
workspaceId, eventId, title, startTime, meetingUrl? |
Wydarzenie kalendarza zaczyna się w skonfigurowanym oknie wyprzedzenia |
MeetingRecordingStopped |
workspaceId, meetingId, title, userNotes, transcript, summaryInstructions? |
Nagranie spotkania się kończy i wyzwala wbudowany potok meeting_summary |
Zdarzenia rigów
Dział zatytułowany „Zdarzenia rigów”| Zdarzenie | Ładunek | Wywoływane, gdy |
|---|---|---|
RigClosedEvent |
workspaceId, rigId, reason |
Rig zniknął, wraz z powodem zamknięcia; konsumenci działają tylko na backendFailure — każdy inny powód wynika z żądania albo towarzyszy mu RigReaped |
RigControlChanged |
workspaceId, rigId, controller? |
Człowiek przejął wyłączną kontrolę nad rigiem albo ją oddał (controller ma wartość null przy zwolnieniu — zdarzenie nie zapisuje, kto oddał kontrolę) |
RigReaped |
workspaceId, rigId, reason, agentId? |
System odzyskał maszynę: bezczynność, TTL albo usunięcie z powodu nacisku na pamięć (raportowane jako idleTimeout), wraz z agentem sterującym |
Otwarcie riga nie jest zdarzeniem. Rozruch się kończy, a wiersz riga przechodzi w stan gotowości; nic nie jest publikowane, więc nie ma RigOpened, którego można by subskrybować.
Podobszary bez żadnych zdarzeń
Dział zatytułowany „Podobszary bez żadnych zdarzeń”Kilka podsystemów, które z zewnątrz wyglądają na sterowane zdarzeniami, takie nie jest. Wymieniamy je tutaj, bo ich brak to decyzja projektowa, którą warto znać, zanim zaczniesz szukać haka:
| Podobszar | Jak zamiast tego zmienia się stan |
|---|---|
| Pamięć | record_memory_fact i pokrewne zapisują i zwracają; przywoływanie czyta. Nic nie publikuje przy fakcie ani konflikcie |
| Orkiestracja | Propozycja, jej zatwierdzenie i wygenerowane uruchomienie potoku to wiersze i wywołania RPC |
| Dokumenty planu | submit_plan zapisuje wersję; zatwierdzenie materializuje orkiestrację. Żadne z nich nie jest ogłaszane |
| Artefakty | publish_artifact / revise_artifact zapisują wersje bezpośrednio |
| Zatwierdzenia | Eskalacja jest obsługiwana wewnątrz usługi przepływu zatwierdzeń |
| Synchronizacja zgłoszeń | Koordynator dostawców subskrybuje powyższe zdarzenia zgłoszeń; przychodzące webhooki są stosowane bezpośrednio |
| Odświeżanie kalendarza | Synchronizacja wykonuje upsert wydarzeń, a klient czyta je ponownie; nie ma rozgłaszania odświeżenia |
| Scalanie worktree | Zapisywane w worktree_merge_log i stamtąd odczytywane |
Subskrybenci
Dział zatytułowany „Subskrybenci”Każdy z poniższych subskrybentów jest konstruowany wewnątrz runCcServer (albo usługi, którą on buduje). Nie ma subskrybenta po stronie klienta.
| Subskrybent | Zdarzenia, które konsumuje |
|---|---|
WorkspaceSeeder |
WorkspaceCreated |
| Zasiewanie startowego zestawu ewaluacyjnego (wbudowane) | WorkspaceCreated |
| Provisionowanie przestrzeni czatu (wbudowane) | SpaceCreated |
PipelineTriggerDispatcher |
Każde DomainEvent; każdy włączony wyzwalacz filtruje według własnego typu |
SubPipelineResumeListener |
PipelineRunCompleted, PipelineRunFailed |
PipelineCostRollupListener |
AgentRunCompleted |
BudgetEvaluationListener |
AgentRunCompleted |
MeetingSummaryReconciler |
PipelineRunCompleted, PipelineRunFailed, PipelineRunCancelled (tylko uruchomienia 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 — wstrzykuje powiadomienie sformułowane prostym językiem na tor sterowania agenta sterującego |
StaleReviewWatcher |
PrHeadChanged — publikuje ReviewBecameStale tylko wtedy, gdy istnieje zakończona recenzja zastąpionego commita |
GitHubPrConversationBridge |
AgentRunCompleted — odpowiedzi wychodzące w rozmowach PR-ów |
NotificationFeedRecorder |
Dwadzieścia dwie klasy zdarzeń powiadomień wymienione poniżej |
RemoteEventForwarder |
Tamte dwadzieścia dwie plus TicketReassigned i każde TaskLifecycleEvent. Trzy zdarzenia członkostwa jedynie unieważniają jego pamięć podręczną członkostwa dla danego obszaru roboczego; nie są przekazywane jako ramki |
LocalRpcServer / RemoteRelayHost |
WorkspaceMemberRemoved — usuwa subskrypcje obszarów roboczych dołączone przez tego użytkownika; gniazdo pozostaje otwarte |
TicketAssigned to sygnał audytowy i powiadomienie oraz wyzwalacz potoku. Przypisanie do agenta zapisuje własność i nie rozpoczyna żadnego uruchomienia — nie istnieje dyspozytor zgłoszeń. Wyjątkiem jest przypisanie do zespołu (ustawione assignedTeamId): TeamRoutingService wysyła lidera zespołu.
TicketRemoteSyncHandler jest zadeklarowany, ale nigdy nie jest konstruowany w kodzie produkcyjnym, więc dziś nie subskrybuje niczego. NotificationEventMapper po stronie klienta jest konstruowany tylko we własnym teście — to pozostałość ścieżki wewnątrzprocesowej sprzed podziału na klienta i serwer. Powiadomienia typu toast na żywo pochodzą z poniższych ramek transmisyjnych.
Mapowanie powiadomień
Dział zatytułowany „Mapowanie powiadomień”Na serwerze NotificationFeedRecorder i RemoteEventForwarder zamieniają te zdarzenia na ramki transmisyjne notifications/* (notification_wire.dart). Na kliencie RpcNotificationMapper → mapNotificationFrame zamienia ramkę na AppNotification.
Kategorii powiadomień jest dokładnie siedemnaście. Kilka zdarzeń mapuje się na więcej niż jedną metodę transmisyjną, bo ramka niesie samo przejście (niepowodzenie kontra powrót do działania), a kategoria niesie pytanie („co robi CI?”).
| Kategoria | Zdarzenie źródłowe | Metoda transmisyjna |
|---|---|---|
newMessage |
MessageReceived |
notifications/message_received (wspomina o Tobie) |
agentRunCompleted |
MessageReceived |
notifications/message_received (is_agent_message, bez wzmianki o Tobie) |
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 |
Ramka przejęcia riga nie jest dostarczana z powrotem do tego, kto przejął ster, zwolnienie jest niefiltrowane (zdarzenie nie zapisuje, kto oddał kontrolę), a tylko zamknięcie typu backendFailure produkuje ramkę rig_closed — pozostałe powody są albo wymuszone żądaniem, albo już objęte ramką odzyskania.
AgentAwaitingInput publikuje AgentAwaitingInputPublisher, który obserwuje rejestry zgód i blokad danych logowania pod kątem nowych wpisów, a o każdym pytaniu ask_user powiadamia go AgentQuestionService. Jedno oczekiwanie to jedno zdarzenie; gdy oczekiwanie się kończy, nic nie jest publikowane.
Pięć kategorii autorskich PR-ów niesie forUserId, więc docierają do autora, a nie do wszystkich w obszarze roboczym.
RemoteEventForwarder przekazuje dodatkowo TicketReassigned jako notifications/ticket_reassigned, a każde TaskLifecycleEvent jako notifications/task_<phase>. Żadne z nich nie mapuje się na kategorię powiadomień — napędzają interfejs na żywo, a nie centrum powiadomień. Zdarzenia członkostwa nie są przekazywane jako ramki: unieważniają pamięć podręczną przekaźnika, a hosty sesji porzucają subskrypcje obszaru roboczego usuniętego członka.