領域事件
事件匯流排
標題為「事件匯流排」的章節DomainEventBus 是一個進程內的廣播式發佈/訂閱匯流排。發佈方呼叫
publish(event);訂閱方則消費帶類型的 on<T>() 串流。每個事件都實作
DomainEvent,並攜帶 occurredAt。
匯流排運行於 cc_server 內部。 每個發佈方與每個存活中的訂閱方都在其中。
沒有任何用戶端能看到這條匯流排——伺服器會把精選的子集以 notifications/*
JSON-RPC 訊框轉發出去,並依工作區成員關係逐一篩選每個訂閱方。
| 指標 | 數量 |
|---|---|
| 具體事件類別 | 54 |
來源檔案(packages/cc_domain/lib/core/domain/events/) |
14(另有 domain_event_bus.dart) |
| 從不直接發佈的抽象基底 | 1(TaskLifecycleEvent,一個 sealed 階層) |
下列目錄中的每個事件都位於那一個目錄裡;沒有在其他地方宣告任何領域事件。
工作區、代理與程式庫事件
標題為「工作區、代理與程式庫事件」的章節| 事件 | 載荷 | 觸發時機 |
|---|---|---|
WorkspaceCreated |
workspaceId |
建立工作區時 |
AgentRunCompleted |
agentId, runId?, workspaceId?, conversationId? |
代理完成一次執行(成功或失敗) |
AgentAwaitingInput |
workspaceId, kind, summary, spaceId?, conversationId?, agentId?, agentName? |
代理開始等待人手處理:一項核准、一個 ask_user 問題,或一次因憑證而被擱置的執行(kind 為 approval、question 或 credential) |
RepoAdded |
repoId, path, workspaceId |
註冊程式庫時;會觸發背景程式碼索引 |
SkillUpdated |
workspaceId, slug, origin, computedHash, scanVerdict? |
安裝或更新技能時,並附帶其供應鏈掃描判定 |
PR 與審查事件
標題為「PR 與審查事件」的章節第一組是 PR 本身的生命週期。第二組是自己提交的 PR 監看:關於你開啟的
PR 的訊號,每個都攜帶 forUserId,讓輪詢能把它們路由給相關的人,而不是廣播出去。
| 事件 | 載荷 | 觸發時機 |
|---|---|---|
PullRequestPublished |
prId, workspaceId, repoOwner, repoName |
代理開啟 PR |
PullRequestStatusChanged |
status, workspaceId, prId?, repoFullName?, prNumber? |
PR 被合併/關閉/開啟/重新開啟/核准 |
PrMerged |
prId, workspaceId, agentId |
PR 被合併——一個僅為分析/通知而保留的窄幅合併專用訊號 |
PrReviewRequested |
workspaceId, repoOwner, repoName, prNumber, prTitle |
GitHub 要求伺服器使用者審查(通知輪詢,review_requested) |
PrMentioned |
workspaceId, repoOwner, repoName, prNumber, prTitle |
伺服器使用者在 PR 上被提及(通知輪詢,mention) |
ExternalPrMerged |
workspaceId, repoOwner, repoName, prNumber, prTitle, mergedByLogin? |
外部追蹤的(非代理)PR 被合併,由 GitHub 通知輪詢偵測到;對應至 prMerged 通知類別 |
ExternalPrDetected |
repoOwner, repoName, prNumber, prTitle, author, workspaceId? |
透過輪詢發現的非代理 PR——僅供管線使用(驅動 external_pr_welcome),不會發出任何通知 |
PrHeadChanged |
repoOwner, repoName, prNumber, prTitle, previousHeadSha, headSha, workspaceId |
PR 的 head SHA 有變動——作者推送了變更 |
ReviewBecameStale |
workspaceId, spaceId, repoOwner, repoName, prNumber, prTitle, reviewedHeadSha, headSha |
已完成的 AI 審查不再描述它所審查的那個 PR。只有在被取代的提交存在審查時才會觸發,因此它維持為一個訊號,而不是每次推送都發出的提示 |
自己提交的 PR 監看
標題為「自己提交的 PR 監看」的章節這六個全部攜帶 workspaceId、repoOwner、repoName、prNumber、prTitle,
以及選用的 forUserId。
| 事件 | 額外載荷 | 觸發時機 |
|---|---|---|
PrMergeReadinessChanged |
ready, reason |
你提交的 PR 變成可合併,或不再可合併 |
PrReviewDecisionChanged |
decision, reviewersRemaining, approverLogin? |
有審查者核准、要求變更,或其核准被撤銷 |
PrChecksStatusChanged |
failing, failingCheckName?, failingCheckUrl? |
CI 變紅,或已恢復 |
PrCommentMentioned |
commentId, authorLogin, bodyPreview, isReviewComment, threadId?, path?, line? |
有人在 PR 留言或審查留言中提及你 |
PrThreadReplied |
commentId, authorLogin, bodyPreview, threadId?, path?, line? |
有人在你所在的審查討論串中回覆 |
PrThreadResolved |
threadId, commentId?, path?, line? |
有人將你所在的審查討論串標記為已解決 |
訊息事件
標題為「訊息事件」的章節| 事件 | 載荷 | 觸發時機 |
|---|---|---|
MessageReceived |
spaceId, messageId, senderName, contentPreview, isAgentMessage, workspaceId?, mentions, requestedByUserId?, senderUserId? |
有新訊息送達 |
SpaceCreated |
spaceId, workspaceId |
建立空間時 |
SpaceDeleted |
spaceId, workspaceId |
刪除空間時 |
SpaceProvisioningChanged |
workspaceId, spaceId, status, step? |
空間的工作區進入佈建、到達某個步驟(複製程式庫、簽出 PR、設定代理),或轉為就緒/失敗——聊天橋接會在其任務卡上敘述這個過程 |
工單事件
標題為「工單事件」的章節| 事件 | 載荷 | 觸發時機 |
|---|---|---|
TicketCreated |
ticketId, workspaceId |
建立工單時 |
TicketAssigned |
ticketId, ticketTitle, ticketBody?, ticketUrl?, assignedAgentId?, assigneeType, assignedTeamId?, workspaceId |
工單指派給某個主體或團隊 |
TicketCompleted |
ticketId |
工作完成(終態) |
TicketFailed |
ticketId, errorMessage |
代理失敗(終態) |
TicketCancelled |
ticketId |
工單取消(終態) |
TicketStatusChanged |
ticketId, workspaceId, from, to |
任何狀態變更 |
TicketReassigned |
ticketId, workspaceId, fromAgentId?, toAgentId? |
工單改派 |
TicketDetailsUpdated |
ticketId, workspaceId |
標題、描述或優先級變更 |
任務生命週期事件
標題為「任務生命週期事件」的章節統一的任務生命週期串流——一次派發的代理執行,以其執行日誌 id 為鍵。每個事件都
攜帶 taskId、每個任務單調遞增的 seq、workspaceId?、agentId? 以及一個
phase;下列各列只列出額外欄位。
| 事件 | 載荷 | 觸發時機 |
|---|---|---|
TaskDispatched |
— | 任務交由後端執行 |
TaskRunning |
— | 任務開始產生輸出 |
TaskWaitingLocalDirectory |
lockedPath, holderTaskId? |
任務被擱置——它所需的磁碟路徑鎖由另一個任務持有 |
TaskProgress |
note? |
執行中途的進度檢查點 |
TaskMessage |
messageType, content |
串流訊息(text/thinking/tool_use/tool_result/error);在 progress 階段下回報 |
TaskCompleted |
summary? |
任務成功完成 |
TaskFailed |
errorMessage |
任務因錯誤而結束 |
TaskCancelled |
— | 任務在完成前被取消 |
TaskPhase 列舉還有一個 queued 成員,但沒有任何東西會發佈排隊事件——並不存在
TaskQueued 類別。任務第一個可觀察到的訊框是 TaskDispatched。
管線事件
標題為「管線事件」的章節| 事件 | 載荷 | 觸發時機 |
|---|---|---|
PipelineRunCompleted |
workspaceId, pipelineRunId, templateId |
執行完成 |
PipelineRunCancelled |
workspaceId, pipelineRunId, templateId |
執行被取消(由使用者或系統發起)——是刻意的停止,不是錯誤 |
PipelineRunFailed |
workspaceId, pipelineRunId, templateId, errorMessage |
執行失敗 |
只有終態轉換會被發佈。既沒有 PipelineRunStarted,也沒有逐步事件:步驟的進度
是透過該次執行自身的資料列,以及它所派發的任何代理的任務生命週期串流,送達
用戶端,而不是透過匯流排。
可觀測性事件
標題為「可觀測性事件」的章節| 事件 | 載荷 | 觸發時機 |
|---|---|---|
ActivityLogged |
id, actorType, actorId?, action, entityType, entityId?, details?, workspaceId?, runId? |
建立稽核軌跡項目 |
BudgetThresholdCrossed |
scopeType, scopeId, spentCents, budgetCents, isHardStop |
支出超過門檻 |
身分與成員關係事件
標題為「身分與成員關係事件」的章節| 事件 | 載荷 | 觸發時機 |
|---|---|---|
WorkspaceMemberAdded |
workspaceId, userId, role |
使用者加入工作區(兌換邀請或由管理員新增) |
WorkspaceMemberRemoved |
workspaceId, userId |
成員被移除——該使用者在該工作區範圍內的使用中工作階段必須立即重新檢查存取權 |
WorkspaceMemberRoleChanged |
workspaceId, userId, role |
成員的角色變更 |
成員關係是匯流排上涵蓋的整個身分層面。使用者建立與裝置撤銷確實會發生,但它們 不會以領域事件形式發佈——撤銷是直接對使用中的工作階段執行,而不是經由匯流排 路由。
日曆與會議事件
標題為「日曆與會議事件」的章節| 事件 | 載荷 | 觸發時機 |
|---|---|---|
CalendarAuthExpired |
workspaceId, accountEmail |
已連接帳戶的 OAuth 重新整理權杖永久失效(每次中斷連線事件只發佈一次) |
MeetingStartingSoon |
workspaceId, eventId, title, startTime, meetingUrl? |
日曆事件將在設定的提前時段內開始 |
MeetingRecordingStopped |
workspaceId, meetingId, title, userNotes, transcript, summaryInstructions? |
會議錄音完成,並觸發內建的 meeting_summary 管線 |
測試機事件
標題為「測試機事件」的章節| 事件 | 載荷 | 觸發時機 |
|---|---|---|
RigClosedEvent |
workspaceId, rigId, reason |
測試機消失了,並附帶其關閉原因;消費者只對 backendFailure 採取行動——其他每個原因都是被請求的,或伴隨著 RigReaped |
RigControlChanged |
workspaceId, rigId, controller? |
有人取得測試機的專屬控制權,或將其交還(釋放時 controller 為 null——事件不會記錄是誰放手的) |
RigReaped |
workspaceId, rigId, reason, agentId? |
系統回收了一部機器:因閒置、TTL 到期或記憶體壓力逐出(回報為 idleTimeout),並附上驅動的代理 |
測試機開啟並不是事件。開機完成後,測試機的資料列會轉為就緒;不會發佈任何
東西,因此沒有 RigOpened 可供訂閱。
完全沒有事件的子領域
標題為「完全沒有事件的子領域」的章節有幾個從外部看起來像是事件驅動的子系統,其實並不是。這裡列出它們,是因為它們的 缺席是一項設計決定,值得你在到處尋找掛鉤之前先了解:
| 子領域 | 狀態改以何種方式推進 |
|---|---|
| 記憶 | record_memory_fact 及其同類函式會寫入並返回;回想則只讀取。事實或衝突都不會發佈任何東西 |
| 編排 | 提案、其核准,以及所產生的管線執行都是資料列與 RPC 呼叫 |
| 計劃文件 | submit_plan 寫入一個修訂;核准則具體化一次編排。兩者都不會對外公告 |
| 產出物 | publish_artifact / revise_artifact 直接寫入修訂 |
| 核准 | 升級是在核准工作流程服務內部處理 |
| 工單同步 | 廠商協調器會訂閱上述工單事件;傳入的 webhook 則直接套用 |
| 日曆重新整理 | 同步會以 upsert 方式寫入事件,用戶端再重新讀取;並沒有重新整理廣播 |
| 工作樹合併 | 記錄在 worktree_merge_log,並從那裡讀取 |
訂閱方
標題為「訂閱方」的章節下列每個訂閱方都在 runCcServer 內部建構(或由它建構的某個服務建構)。沒有
用戶端訂閱方。
| 訂閱方 | 它消費的事件 |
|---|---|
WorkspaceSeeder |
WorkspaceCreated |
| 入門評估套件植入(內聯) | WorkspaceCreated |
| 聊天空間佈建(內聯) | SpaceCreated |
PipelineTriggerDispatcher |
每個 DomainEvent;每個已啟用的觸發器會篩選自己的類型 |
SubPipelineResumeListener |
PipelineRunCompleted, PipelineRunFailed |
PipelineCostRollupListener |
AgentRunCompleted |
BudgetEvaluationListener |
AgentRunCompleted |
MeetingSummaryReconciler |
PipelineRunCompleted, PipelineRunFailed, PipelineRunCancelled(僅限 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——以淺白用語在驅動代理的操控通道上注入一則提示 |
StaleReviewWatcher |
PrHeadChanged——僅當被取代的提交存在已完成的審查時,才發佈 ReviewBecameStale |
GitHubPrConversationBridge |
AgentRunCompleted——對外的 PR 對話回覆 |
NotificationFeedRecorder |
下列二十二個通知類別事件 |
RemoteEventForwarder |
上述二十二個,加上 TicketReassigned 及每個 TaskLifecycleEvent。三個成員關係事件只會使其按工作區的成員關係快取失效;它們不會以訊框轉發 |
LocalRpcServer / RemoteRelayHost |
WorkspaceMemberRemoved——移除該使用者已掛載的工作區訂閱;Socket 保持開啟 |
TicketAssigned 既是稽核與通知訊號,也是管線觸發器。指派給代理會記錄擁有權,但
不會啟動任何執行——並不存在工單調度器。指派給團隊(已設定 assignedTeamId)則
是例外:TeamRoutingService 會派發團隊負責人。
TicketRemoteSyncHandler 已被宣告,但從未在生產程式碼中建構,因此它今日不訂閱
任何東西。用戶端的 NotificationEventMapper 只在它自己的測試中建構——它是
用戶端/伺服器拆分之前進程內路徑的殘留物。即時彈出提示則來自下列的傳輸訊框。
通知對應
標題為「通知對應」的章節在伺服器上,NotificationFeedRecorder 與 RemoteEventForwarder 會把這些事件
轉成 notifications/* 傳輸訊框(notification_wire.dart)。在用戶端,
RpcNotificationMapper → mapNotificationFrame 會把訊框轉成
AppNotification。
通知類別恰好有十七個。有幾個事件會對應到多於一種傳輸方法,因為訊框承載的 是變化方向(失敗還是已恢復),而類別承載的是問題本身(「CI 現在怎麼了?」)。
| 類別 | 來源事件 | 傳輸方法 |
|---|---|---|
newMessage |
MessageReceived |
notifications/message_received(提及你) |
agentRunCompleted |
MessageReceived |
notifications/message_received(is_agent_message,未提及你) |
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 |
測試機的接管訊框不會回送給取得控制權的那個人;釋放則不經過篩選(事件不會記錄
是誰放手);而且只有 backendFailure 關閉會產生 rig_closed 訊框——其他原因
不是被請求的,就是已由回收訊框涵蓋。
AgentAwaitingInput 由 AgentAwaitingInputPublisher 發佈:它會監察核准登記及
憑證封鎖登記中的新項目,並由 AgentQuestionService 告知每個 ask_user 問題。
一次等待就是一個事件;等待結束時不會發佈任何事件。
五個自己提交的 PR 類別都攜帶 forUserId,因此它們會送達作者,而不是工作區內的
每個人。
RemoteEventForwarder 另外會把 TicketReassigned 轉發為
notifications/ticket_reassigned,並把每個 TaskLifecycleEvent 轉發為
notifications/task_<phase>。兩者都不會對應到通知類別——它們驅動即時 UI,
而不是通知中心。成員關係事件不會以訊框轉發:它們會使轉發器的快取失效,而工作
階段主機則會捨棄被移除成員的工作區訂閱。