領域事件
事件匯流排
標題為「事件匯流排」的章節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,一個密封階層) |
下文目錄中的每個事件都位於這一個目錄;沒有在其他地方宣告的領域事件。
工作區、代理與儲存庫事件
標題為「工作區、代理與儲存庫事件」的章節| 事件 | 承載內容 | 觸發時機 |
|---|---|---|
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,而不是通知中心。成員資格事件不會以訊框轉送:它們會使轉送器的快取失效,而工作階段主機則會捨棄被移除成員的工作區訂閱。