Skip to content

Domain events

Domain events are the decoupling mechanism that lets features communicate without direct dependencies. Instead of feature A calling feature B directly, feature A publishes an event, and feature B subscribes to it.

The DomainEventBus is an in-process broadcast publish/subscribe bus. Features call publish(event) to emit events; subscribers consume a typed on<T>() stream.

Every event implements DomainEvent and exposes occurredAt.

Without events, cross-feature communication requires direct imports. The dashboard would need to import the agents feature, the pipelines feature, and so on — creating a tangled dependency graph.

Events break this coupling. The dashboard subscribes to events from multiple features without importing any of them. Similarly, the notification system maps events to desktop notifications without knowing about any specific feature.

EventTriggerConsumers
WorkspaceCreatedWorkspace is createdCEO agent seeder
AgentRunCompletedAn agent finishes a runAnalytics, notifications, cost tracking, budget enforcement
RepoAddedA repo is registeredBackground code indexing
EventTriggerConsumers
PullRequestPublishedAn agent opens a PRNotifications
PullRequestStatusChangedPR merged/closed/opened/reopenedPipeline triggers
PrMergedPR is mergedAnalytics, notifications
ExternalPrDetectedNon-agent PR found via pollingDashboard
EventTriggerConsumers
MessageReceivedA new message arrivesDesktop notifications
ConversationDeletedA conversation is deletedWorktree garbage collection
EventTriggerConsumers
TicketCreatedA ticket is createdNotifications
TicketAssignedA ticket is assigned to an agentTicket dispatcher
TicketStartedWork begins on a ticketAnalytics
TicketCompletedA ticket is donePipeline triggers, analytics
TicketFailedA ticket’s work failedNotifications
TicketCancelledA ticket is cancelledCleanup
TicketStatusChangedAny ticket state changeAudit trail
TicketReassignedTicket reassigned to another agentTicket dispatcher
TicketDelegatedTicket delegated by an agentNotifications
TicketCollaboratorAddedA collaborator joins a ticketNotifications
TicketDetailsUpdatedTicket metadata changesExternal sync
EventTriggerConsumers
PipelineRunStartedA pipeline run beginsNotifications
PipelineStepStartedA step beginsRun tracking
PipelineStepCompletedA step finishesRun tracking
PipelineStepFailedA step failsNotifications
PipelineRunCompletedA run finishesAnalytics, notifications
PipelineRunFailedA run failsNotifications
EventTriggerConsumers
ActivityLoggedAn audit trail entry is createdActivity feed
WorktreeMergedA worktree merge completesCleanup
BudgetThresholdCrossedSpend exceeds a thresholdNotifications
EventTriggerConsumers
AchievementUnlockedAn agent earns a badgeNotifications

Pipeline triggers subscribe to domain events and auto-start matching pipelines. The PipelineTriggerDispatcher:

  1. Subscribes to all event types
  2. For each event, checks enabled triggers that match the event type
  3. Applies the trigger’s payload match filter
  4. Starts a pipeline run for each matching trigger

This is how you get event-driven workflows — a PullRequestPublished event can trigger a review pipeline, a TicketAssigned event can trigger a dispatch pipeline, etc.

Events are wired in main.dart:

  • ceoAgentSeedProvider listens for WorkspaceCreated to seed the CEO agent
  • NotificationEventMapper subscribes to events and produces desktop notifications
  • PipelineTriggerDispatcher subscribes to events for auto-starting pipelines
  • Various reconcilers listen for events at startup