Доменні події
Шина подій
Розділ із назвою «Шина подій»DomainEventBus — це внутрішньопроцесна широкомовна шина публікації/підписки. Видавці викликають publish(event); підписники споживають типізований потік on<T>(). Кожна подія реалізує DomainEvent і несе occurredAt.
Шина працює всередині cc_server. Кожен видавець і кожен живий підписник живуть там. Жоден клієнт не бачить цієї шини — сервер пересилає дібрану підмножину як фрейми JSON-RPC notifications/*, відфільтровані для кожного підписника за членством у робочому просторі.
| Показник | Кількість |
|---|---|
| Конкретні класи подій | 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? |
Навичку встановлено або оновлено; подія несе вердикт сканування її ланцюга постачання |
Події pull request-ів і рев’ю
Розділ із назвою «Події pull request-ів і рев’ю»Перший блок — це власний життєвий цикл 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 |
Головний SHA PR зрушив — автор зробив push |
ReviewBecameStale |
workspaceId, spaceId, repoOwner, repoName, prNumber, prTitle, reviewedHeadSha, headSha |
Завершена ШІ-перевірка більше не описує PR, який вона перевіряла. Спрацьовує лише тоді, коли для заміненого коміту існує перевірка, тож залишається сигналом, а не нагадуванням на кожен push |
Стеження за авторськими 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? |
Робочий простір простора входить у підготовку, досягає кроку (клонування репозиторію, checkout PR, налаштування агента) або переходить у ready/failed — чат-міст описує це на своїй картці завдання |
Події тікетів
Розділ із назвою «Події тікетів»| Подія | Корисне навантаження | Спрацьовує, коли |
|---|---|---|
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 |
Зміна назви, опису або пріоритету |
Події життєвого циклу завдань
Розділ із назвою «Події життєвого циклу завдань»Уніфікований потік життєвого циклу завдань — один відправлений запуск агента, ключем якого є ідентифікатор його журналу запуску. Кожна подія несе 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 |
Події rig
Розділ із назвою «Події rig»| Подія | Корисне навантаження | Спрацьовує, коли |
|---|---|---|
RigClosedEvent |
workspaceId, rigId, reason |
Rig зник разом із причиною закриття; споживачі реагують лише на backendFailure — кожна інша причина або запитана, або супроводжується RigReaped |
RigControlChanged |
workspaceId, rigId, controller? |
Людина взяла ексклюзивне керування rig або повернула його (controller дорівнює null під час повернення — подія не фіксує, хто відпустив) |
RigReaped |
workspaceId, rigId, reason, agentId? |
Система повернула машину: простій, TTL або витіснення через тиск на пам’ять (повідомляється як idleTimeout), разом з агентом, що керував |
Відкриття rig не є подією. Завантаження завершується, і рядок rig переходить у стан ready; нічого не публікується, тож підписатися на RigOpened немає на що.
Піддомени без жодних подій
Розділ із назвою «Піддомени без жодних подій»Кілька підсистем, які ззовні виглядають керованими подіями, такими не є. Вони перелічені тут, бо їхня відсутність — це проєктне рішення, яке варто знати, перш ніж шукати точку підключення:
| Піддомен | Як натомість рухається стан |
|---|---|
| Пам’ять | record_memory_fact та подібні інструменти пишуть і повертають результат; пригадування читає. Ніщо не публікує подію про факт чи конфлікт |
| Оркестрація | Пропозиція, її затвердження та згенерований запуск конвеєра — це рядки й виклики RPC |
| Документи планів | submit_plan пише ревізію; затвердження матеріалізує оркестрацію. Ні про перше, ні про друге не оголошується |
| Артефакти | publish_artifact / revise_artifact пишуть ревізії безпосередньо |
| Схвалення | Ескалацію обробляє всередині сервіс робочого процесу схвалення |
| Синхронізація тікетів | Координатор постачальників підписується на події тікетів вище; вхідні вебхуки застосовуються безпосередньо |
| Оновлення календаря | Синхронізація робить upsert подій, а клієнт перечитує їх; широкомовного сповіщення про оновлення немає |
| Злиття worktree | Записуються в worktree_merge_log і читаються звідти |
Підписники
Розділ із назвою «Підписники»Кожен підписник нижче створюється всередині runCcServer (або сервісу, який він будує). Клієнтських підписників немає.
| Підписник | Події, які він споживає |
|---|---|
WorkspaceSeeder |
WorkspaceCreated |
| Засівання стартового набору eval (вбудовано) | 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 — відкидає підписки на робочі простори, прикріплені до цього користувача; сокет залишається відкритим |
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 |
Фрейм перехоплення керування rig не доставляється назад тому, хто взяв кермо; повернення не фільтрується (подія не фіксує, хто відпустив), і лише закриття з backendFailure породжує фрейм rig_closed — інші причини або запитані, або вже покриті фреймом збирання.
AgentAwaitingInput публікує AgentAwaitingInputPublisher: він стежить за новими записами в реєстрах схвалень і блокувань через облікові дані, а про кожне запитання ask_user йому повідомляє AgentQuestionService. Одне очікування — одна подія; коли очікування завершується, нічого не публікується.
П’ять категорій авторських PR несуть forUserId, тож доходять до автора, а не до всіх у робочому просторі.
RemoteEventForwarder додатково пересилає TicketReassigned як notifications/ticket_reassigned, а кожен TaskLifecycleEvent — як notifications/task_<phase>. Жодне з них не відображається на категорію сповіщень — вони керують живим інтерфейсом, а не центром сповіщень. Події членства не пересилаються як фрейми: вони анулюють кеш пересилача, а хости сесій відкидають підписки на робочі простори видаленого учасника.