Domain-Ereignisse
Ereignisbus
Abschnitt betitelt „Ereignisbus“DomainEventBus ist ein prozessinterner Broadcast-Bus nach dem Publish/Subscribe-Muster. Publisher rufen publish(event) auf; Subscriber konsumieren einen typisierten Strom on<T>(). Jedes Ereignis implementiert DomainEvent und trägt occurredAt.
Der Bus läuft in cc_server. Jeder Publisher und jeder lebende Subscriber lebt dort. Kein Client sieht diesen Bus. Der Server leitet eine kuratierte Teilmenge als JSON-RPC-Frames notifications/* weiter, je Subscriber nach der Mitgliedschaft im Arbeitsbereich gefiltert.
| Kennzahl | Anzahl |
|---|---|
| Konkrete Ereignisklassen | 54 |
Quelldateien (packages/cc_domain/lib/core/domain/events/) |
14 (plus domain_event_bus.dart) |
| Abstrakte Basen, die nie direkt veröffentlicht werden | 1 (TaskLifecycleEvent, eine sealed Hierarchie) |
Jedes Ereignis im Katalog unten lebt in genau diesem Verzeichnis; nirgendwo sonst sind Domain-Ereignisse deklariert.
Ereignisse zu Arbeitsbereich, Agent und Repo
Abschnitt betitelt „Ereignisse zu Arbeitsbereich, Agent und Repo“| Ereignis | Payload | Auslöser |
|---|---|---|
WorkspaceCreated |
workspaceId |
Ein Arbeitsbereich wird angelegt |
AgentRunCompleted |
agentId, runId?, workspaceId?, conversationId? |
Ein Agent beendet einen Lauf (Erfolg oder Fehler) |
AgentAwaitingInput |
workspaceId, kind, summary, spaceId?, conversationId?, agentId?, agentName? |
Ein Agent beginnt, auf einen Menschen zu warten: auf eine Freigabe, auf eine Antwort auf eine ask_user-Frage oder mit einem Lauf, der an Zugangsdaten festhängt (kind ist approval, question oder credential) |
RepoAdded |
repoId, path, workspaceId |
Ein Repo wird registriert; stößt die Code-Indizierung im Hintergrund an |
SkillUpdated |
workspaceId, slug, origin, computedHash, scanVerdict? |
Ein Skill wird installiert oder aktualisiert und trägt das Urteil seines Supply-Chain-Scans |
PR- und Review-Ereignisse
Abschnitt betitelt „PR- und Review-Ereignisse“Der erste Block ist der eigene Lebenszyklus des PR. Der zweite ist die Beobachtung eigener PRs: Signale zu einem PR, den du geöffnet hast. Jedes trägt forUserId, damit die Abfrage sie der betroffenen Person zustellt, statt sie an alle zu senden.
| Ereignis | Payload | Auslöser |
|---|---|---|
PullRequestPublished |
prId, workspaceId, repoOwner, repoName |
Ein Agent öffnet einen PR |
PullRequestStatusChanged |
status, workspaceId, prId?, repoFullName?, prNumber? |
PR gemergt, geschlossen, geöffnet, wieder geöffnet oder freigegeben |
PrMerged |
prId, workspaceId, agentId |
Der PR ist gemergt. Ein schmales Signal nur für den Merge, das für Analysen und Benachrichtigungen bleibt |
PrReviewRequested |
workspaceId, repoOwner, repoName, prNumber, prTitle |
GitHub bittet um das Review des Server-Benutzers (Abfrage der Benachrichtigungen, review_requested) |
PrMentioned |
workspaceId, repoOwner, repoName, prNumber, prTitle |
Der Server-Benutzer wird in einem PR erwähnt (Abfrage der Benachrichtigungen, mention) |
ExternalPrMerged |
workspaceId, repoOwner, repoName, prNumber, prTitle, mergedByLogin? |
Ein extern verfolgter PR (kein Agent) wurde gemergt, erkannt durch die Abfrage der GitHub-Benachrichtigungen; wird auf die Benachrichtigungskategorie prMerged abgebildet |
ExternalPrDetected |
repoOwner, repoName, prNumber, prTitle, author, workspaceId? |
PR ohne Agent per Abfrage gefunden. Nur für Pipelines (treibt external_pr_welcome an), löst keine Benachrichtigung aus |
PrHeadChanged |
repoOwner, repoName, prNumber, prTitle, previousHeadSha, headSha, workspaceId |
Die Head-SHA des PR hat sich bewegt. Der Autor hat gepusht |
ReviewBecameStale |
workspaceId, spaceId, repoOwner, repoName, prNumber, prTitle, reviewedHeadSha, headSha |
Ein abgeschlossenes KI-Review beschreibt den geprüften PR nicht mehr. Es feuert nur, wenn für den ersetzten Commit ein Review existiert, und bleibt so ein Signal statt eines Pings bei jedem Push |
Die Beobachtung eigener PRs
Abschnitt betitelt „Die Beobachtung eigener PRs“Alle sechs tragen workspaceId, repoOwner, repoName, prNumber, prTitle und ein optionales forUserId.
| Ereignis | Zusätzlicher Payload | Auslöser |
|---|---|---|
PrMergeReadinessChanged |
ready, reason |
Ein PR, den du verfasst hast, wurde mergebar oder war es nicht mehr |
PrReviewDecisionChanged |
decision, reviewersRemaining, approverLogin? |
Ein Reviewer hat freigegeben, Änderungen angefordert, oder eine Freigabe wurde verworfen |
PrChecksStatusChanged |
failing, failingCheckName?, failingCheckUrl? |
Die CI wurde rot oder hat sich erholt |
PrCommentMentioned |
commentId, authorLogin, bodyPreview, isReviewComment, threadId?, path?, line? |
Jemand hat dich in einem PR-Kommentar oder Review-Kommentar erwähnt |
PrThreadReplied |
commentId, authorLogin, bodyPreview, threadId?, path?, line? |
Jemand hat in einem Review-Thread geantwortet, in dem du bist |
PrThreadResolved |
threadId, commentId?, path?, line? |
Jemand hat einen Review-Thread aufgelöst, in dem du bist |
Nachrichtenereignisse
Abschnitt betitelt „Nachrichtenereignisse“| Ereignis | Payload | Auslöser |
|---|---|---|
MessageReceived |
spaceId, messageId, senderName, contentPreview, isAgentMessage, workspaceId?, mentions, requestedByUserId?, senderUserId? |
Eine neue Nachricht trifft ein |
SpaceCreated |
spaceId, workspaceId |
Ein Bereich wird angelegt |
SpaceDeleted |
spaceId, workspaceId |
Ein Bereich wird gelöscht |
SpaceProvisioningChanged |
workspaceId, spaceId, status, step? |
Der Arbeitsbereich des Bereichs geht in die Bereitstellung, erreicht einen Schritt (ein Repo klonen, einen PR auschecken, einen Agenten einrichten) oder wechselt auf bereit bzw. fehlgeschlagen. Die Chat-Brücke erzählt das auf ihrer Aufgabenkarte |
Ticket-Ereignisse
Abschnitt betitelt „Ticket-Ereignisse“| Ereignis | Payload | Auslöser |
|---|---|---|
TicketCreated |
ticketId, workspaceId |
Ein Ticket wird angelegt |
TicketAssigned |
ticketId, ticketTitle, ticketBody?, ticketUrl?, assignedAgentId?, assigneeType, assignedTeamId?, workspaceId |
Ticket einem Prinzipal oder einem Team zugewiesen |
TicketCompleted |
ticketId |
Die Arbeit endet (Endzustand) |
TicketFailed |
ticketId, errorMessage |
Der Agent schlägt fehl (Endzustand) |
TicketCancelled |
ticketId |
Ticket abgebrochen (Endzustand) |
TicketStatusChanged |
ticketId, workspaceId, from, to |
Jede Statusänderung |
TicketReassigned |
ticketId, workspaceId, fromAgentId?, toAgentId? |
Ticket neu zugewiesen |
TicketDetailsUpdated |
ticketId, workspaceId |
Titel, Beschreibung oder Priorität ändert sich |
Ereignisse zum Aufgaben-Lebenszyklus
Abschnitt betitelt „Ereignisse zum Aufgaben-Lebenszyklus“Der einheitliche Strom zum Aufgaben-Lebenszyklus: ein ausgeführter Agentenlauf, über die Id seines Laufprotokolls identifiziert. Jedes Ereignis trägt taskId, eine monotone seq je Aufgabe, workspaceId?, agentId? und eine phase. Die Zeilen unten nennen nur die zusätzlichen Felder.
| Ereignis | Payload | Auslöser |
|---|---|---|
TaskDispatched |
— | Aufgabe zur Ausführung an ein Backend übergeben |
TaskRunning |
— | Aufgabe hat begonnen, Ausgabe zu erzeugen |
TaskWaitingLocalDirectory |
lockedPath, holderTaskId? |
Aufgabe geparkt. Eine andere Aufgabe hält die Pfad-Sperre auf der Platte, die sie braucht |
TaskProgress |
note? |
Fortschritts-Checkpoint mitten im Lauf |
TaskMessage |
messageType, content |
Gestreamte Nachricht (text/thinking/tool_use/tool_result/error); gemeldet unter der Phase progress |
TaskCompleted |
summary? |
Aufgabe erfolgreich beendet |
TaskFailed |
errorMessage |
Aufgabe mit einem Fehler beendet |
TaskCancelled |
— | Aufgabe vor dem Abschluss abgebrochen |
Das Enum TaskPhase hat auch den Wert queued, veröffentlicht aber niemand ein Ereignis dafür. Eine Klasse TaskQueued gibt es nicht. Der erste beobachtbare Frame einer Aufgabe ist TaskDispatched.
Pipeline-Ereignisse
Abschnitt betitelt „Pipeline-Ereignisse“| Ereignis | Payload | Auslöser |
|---|---|---|
PipelineRunCompleted |
workspaceId, pipelineRunId, templateId |
Der Lauf endet |
PipelineRunCancelled |
workspaceId, pipelineRunId, templateId |
Lauf abgebrochen (durch dich oder das System). Ein bewusster Stopp, kein Fehler |
PipelineRunFailed |
workspaceId, pipelineRunId, templateId, errorMessage |
Der Lauf schlägt fehl |
Veröffentlicht werden nur Übergänge in einen Endzustand. PipelineRunStarted gibt es nicht, und auch kein Ereignis je Schritt: Der Fortschritt eines Schritts erreicht die Clients über die eigenen Zeilen des Laufs und über den Strom zum Aufgaben-Lebenszyklus des Agenten, den der Schritt gestartet hat, nicht über den Bus.
Beobachtbarkeitsereignisse
Abschnitt betitelt „Beobachtbarkeitsereignisse“| Ereignis | Payload | Auslöser |
|---|---|---|
ActivityLogged |
id, actorType, actorId?, action, entityType, entityId?, details?, workspaceId?, runId? |
Eintrag im Audit-Protokoll angelegt |
BudgetThresholdCrossed |
scopeType, scopeId, spentCents, budgetCents, isHardStop |
Die Ausgaben überschreiten die Schwelle |
Ereignisse zu Identität und Mitgliedschaft
Abschnitt betitelt „Ereignisse zu Identität und Mitgliedschaft“| Ereignis | Payload | Auslöser |
|---|---|---|
WorkspaceMemberAdded |
workspaceId, userId, role |
Ein Benutzer tritt einem Arbeitsbereich bei (Einlösung einer Einladung oder Hinzufügen durch einen Admin) |
WorkspaceMemberRemoved |
workspaceId, userId |
Ein Mitglied wird entfernt. Lebende Sitzungen dieses Benutzers, die auf den Arbeitsbereich beschränkt sind, müssen den Zugriff sofort neu prüfen |
WorkspaceMemberRoleChanged |
workspaceId, userId, role |
Die Rolle eines Mitglieds ändert sich |
Die Mitgliedschaft ist auf dem Bus die gesamte Identitätsfläche. Benutzer werden angelegt und Geräte widerrufen, als Domain-Ereignisse wird das aber nicht veröffentlicht. Der Widerruf wird direkt an den lebenden Sitzungen durchgesetzt, statt über den Bus geleitet zu werden.
Ereignisse zu Kalender und Besprechungen
Abschnitt betitelt „Ereignisse zu Kalender und Besprechungen“| Ereignis | Payload | Auslöser |
|---|---|---|
CalendarAuthExpired |
workspaceId, accountEmail |
Das OAuth-Refresh-Token eines verbundenen Kontos ist dauerhaft ungültig (einmal je Trennung veröffentlicht) |
MeetingStartingSoon |
workspaceId, eventId, title, startTime, meetingUrl? |
Ein Kalendertermin beginnt innerhalb des konfigurierten Vorlauffensers |
MeetingRecordingStopped |
workspaceId, meetingId, title, userNotes, transcript, summaryInstructions? |
Eine Besprechungsaufnahme endet und stößt die eingebaute Pipeline meeting_summary an |
Rig-Ereignisse
Abschnitt betitelt „Rig-Ereignisse“| Ereignis | Payload | Auslöser |
|---|---|---|
RigClosedEvent |
workspaceId, rigId, reason |
Ein Rig ist weg, mit seinem Schließgrund. Konsumenten handeln nur bei backendFailure. Jeder andere Grund ist angefordert oder von einem RigReaped begleitet |
RigControlChanged |
workspaceId, rigId, controller? |
Ein Mensch hat die exklusive Steuerung eines Rigs übernommen oder zurückgegeben (controller ist bei der Rückgabe null. Das Ereignis hält nicht fest, wer losgelassen hat) |
RigReaped |
workspaceId, rigId, reason, agentId? |
Das System hat eine Maschine zurückgeholt: Leerlauf, TTL oder Verdrängung unter Speicherdruck (gemeldet als idleTimeout), zusammen mit dem antreibenden Agenten |
Das Öffnen eines Rigs ist kein Ereignis. Der Boot endet, und die Zeile des Rigs wechselt auf bereit. Veröffentlicht wird nichts, ein RigOpened zum Abonnieren gibt es also nicht.
Subdomänen ganz ohne Ereignisse
Abschnitt betitelt „Subdomänen ganz ohne Ereignisse“Mehrere Subsysteme sehen von außen ereignisgetrieben aus und sind es nicht. Sie stehen hier, weil ihr Fehlen eine Entwurfsentscheidung ist, die du kennen solltest, bevor du nach einem Hook suchst:
| Subdomäne | Wie der Zustand stattdessen weitergeht |
|---|---|
| Gedächtnis | record_memory_fact und Verwandte schreiben und geben zurück; der Abruf liest. Weder auf einem Fakt noch bei einem Konflikt wird etwas veröffentlicht |
| Orchestrierung | Der Vorschlag, seine Freigabe und der erzeugte Pipeline-Lauf sind Zeilen und RPC-Aufrufe |
| Plandokumente | submit_plan schreibt eine Revision; die Freigabe materialisiert eine Orchestrierung. Angekündigt wird beides nicht |
| Artefakte | publish_artifact / revise_artifact schreiben Revisionen direkt |
| Freigaben | Die Eskalation läuft im Dienst des Freigabe-Workflows |
| Ticket-Synchronisierung | Der Anbieter-Koordinator abonniert die Ticket-Ereignisse oben; eingehende Webhooks werden direkt angewandt |
| Kalenderaktualisierung | Eine Synchronisierung schreibt die Termine per Upsert, der Client liest neu. Eine Broadcast-Aktualisierung gibt es nicht |
| Worktree-Merges | In worktree_merge_log festgehalten und von dort gelesen |
Abonnenten
Abschnitt betitelt „Abonnenten“Jeder Abonnent unten wird in runCcServer erzeugt (oder in einem Dienst, den diese Funktion baut). Clientseitig abonniert niemand.
| Abonnent | Ereignisse, die er entgegennimmt |
|---|---|
WorkspaceSeeder |
WorkspaceCreated |
| Anlegen der Eval-Suiten für den Einstieg (inline) | WorkspaceCreated |
| Bereitstellung des Chat-Bereichs (inline) | SpaceCreated |
PipelineTriggerDispatcher |
Jedes DomainEvent; jeder aktivierte Trigger filtert auf seinen eigenen Typ |
SubPipelineResumeListener |
PipelineRunCompleted, PipelineRunFailed |
PipelineCostRollupListener |
AgentRunCompleted |
BudgetEvaluationListener |
AgentRunCompleted |
MeetingSummaryReconciler |
PipelineRunCompleted, PipelineRunFailed, PipelineRunCancelled (nur Läufe von 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. Schleust einen Hinweis in Klartext in den Steuerkanal des antreibenden Agenten |
StaleReviewWatcher |
PrHeadChanged. Veröffentlicht ReviewBecameStale nur, wenn für den ersetzten Commit ein abgeschlossenes Review existiert |
GitHubPrConversationBridge |
AgentRunCompleted. Ausgehende Antworten der PR-Unterhaltung |
NotificationFeedRecorder |
Die 22 Ereignisse der Benachrichtigungsklasse unten |
RemoteEventForwarder |
Diese 22 plus TicketReassigned und jedes TaskLifecycleEvent. Die drei Mitgliedschaftsereignisse machen nur seinen Mitgliedschafts-Cache je Arbeitsbereich ungültig; als Frames werden sie nicht weitergeleitet |
LocalRpcServer / RemoteRelayHost |
WorkspaceMemberRemoved. Verwirft die angehängten Arbeitsbereichsabonnements dieses Benutzers; der Socket bleibt offen |
TicketAssigned ist ein Signal für Audit und Benachrichtigung und ein Pipeline-Trigger. Die Zuweisung an einen Agenten vermerkt die Zuständigkeit und startet keinen Lauf. Einen Ticket-Dispatcher gibt es nicht. Die Ausnahme ist die Zuweisung an ein Team (gesetztes assignedTeamId): TeamRoutingService startet die Teamleitung.
TicketRemoteSyncHandler ist deklariert und wird im Produktionscode nie erzeugt, also abonniert er heute nichts. Der clientseitige NotificationEventMapper wird nur in seinem eigenen Test erzeugt. Er ist der Rest des prozessinternen Pfads von vor der Aufteilung in Client und Server. Lebende Toasts kommen aus den Wire-Frames unten.
Abbildung auf Benachrichtigungen
Abschnitt betitelt „Abbildung auf Benachrichtigungen“Auf dem Server machen NotificationFeedRecorder und RemoteEventForwarder aus diesen Ereignissen Wire-Frames notifications/* (notification_wire.dart). Auf dem Client macht RpcNotificationMapper → mapNotificationFrame aus einem Frame eine AppNotification.
Es gibt genau siebzehn Benachrichtigungskategorien. Mehrere Ereignisse bilden auf mehr als eine Wire-Methode ab, weil der Frame die Kante trägt (fehlgeschlagen gegenüber erholt) und die Kategorie die Frage („was macht die CI?“).
| Kategorie | Quellereignis | Wire-Methode |
|---|---|---|
newMessage |
MessageReceived |
notifications/message_received (erwähnt dich) |
agentRunCompleted |
MessageReceived |
notifications/message_received (is_agent_message, ohne Erwähnung von dir) |
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 |
Der Frame zur Rig-Übernahme geht nicht an die Person zurück, die das Steuer
übernommen hat. Eine Rückgabe ist ungefiltert (das Ereignis hält nicht fest,
wer losgelassen hat), und nur ein Schließen mit backendFailure erzeugt einen
Frame rig_closed. Die anderen Gründe sind angefordert oder schon vom Frame
zum Zurückholen abgedeckt.
AgentAwaitingInput wird von AgentAwaitingInputPublisher veröffentlicht. Er
beobachtet die Register für Freigaben und für blockierte Zugangsdaten auf neue
Einträge, und AgentQuestionService meldet ihm jede ask_user-Frage. Eine
Wartezeit ergibt ein Ereignis; wenn sie endet, wird nichts veröffentlicht.
Die fünf Kategorien zur Beobachtung eigener PRs tragen forUserId und erreichen damit den Autor, nicht jeden im Arbeitsbereich.
RemoteEventForwarder leitet zusätzlich TicketReassigned als notifications/ticket_reassigned weiter und jedes TaskLifecycleEvent als notifications/task_<phase>. Auf eine Benachrichtigungskategorie bildet keines davon ab. Sie treiben die Live-Oberfläche, nicht die Benachrichtigungszentrale. Mitgliedschaftsereignisse werden nicht als Frames weitergeleitet: Sie machen den Cache des Forwarders ungültig, und die Sitzungs-Hosts verwerfen die Arbeitsbereichsabonnements des entfernten Mitglieds.