# How Event Parent-Child Relationships Are Tracked in Bubus: A Deep Dive into the EventBus Tree

> Discover how Bubus tracks event parent-child relationships using event_parent_id metadata and context-aware dispatch logic for seamless event management. Learn more about the EventBus tree.

- Repository: [Browser Use/bubus](https://github.com/browser-use/bubus)
- Tags: deep-dive
- Published: 2026-02-26

---

**Bubus automatically tracks event parent-child relationships by recording `event_parent_id` metadata on every `BaseEvent` and aggregating child events through context-aware dispatch logic in the `EventBus` service.**

The `browser-use/bubus` library implements an implicit event tree that requires no manual bookkeeping. When events are dispatched from within handlers, the system automatically wires parent and child relationships using context variables and metadata fields defined in the core models.

## The Metadata Foundation: event_parent_id and event_children

Bubus builds the event tree on two metadata properties attached to every `BaseEvent` instance.

### The event_parent_id Field

In [`bubus/models.py`](https://github.com/browser-use/bubus/blob/main/bubus/models.py), the `BaseEvent` class defines `event_parent_id` as a Pydantic field:

```python

# bubus/models.py L38-L40

event_parent_id: UUIDStr | None = Field(
    default=None, 
    description='ID of the parent event that triggered this event'
)

```

This field stores the UUID of the event that caused the current event to be dispatched. When `None`, the event is treated as a root node in the event tree.

### The event_children Property

The same file defines a computed property that aggregates all child events from every `EventResult` attached to the parent:

```python

# bubus/models.py L28-L34

@property
def event_children(self) -> list['BaseEvent[Any]']:
    children: list[BaseEvent[Any]] = []
    for event_result in self.event_results.values():
        children.extend(event_result.event_children)
    return children

```

This property walks all handler results attached to the event and concatenates their `event_children` lists, providing a flat list of direct descendants.

## How Parent IDs Are Assigned During Dispatch

The `EventBus.dispatch()` method in [`bubus/service.py`](https://github.com/browser-use/bubus/blob/main/bubus/service.py) implements the logic that automatically assigns parent IDs using a context variable.

When an event is dispatched, the system checks `_current_event_context`, which holds the event currently being processed:

```python

# bubus/service.py L14-L19

if event.event_parent_id is None:
    current_event = _current_event_context.get()
    if current_event is not None:
        event.event_parent_id = current_event.event_id

```

This ensures that any event dispatched from within a handler automatically inherits the handler's event ID as its parent ID, unless explicitly overridden.

## Recording Child Events in Real-Time

While a handler executes, Bubus tracks child events through three context variables:

- `_current_event_context` – the event being processed
- `_current_handler_id_context` – the unique ID of the executing handler
- `inside_handler_context` – boolean flag indicating handler execution context

When a new event is dispatched from within a handler, the system appends it to the `event_children` list of the handler's `EventResult`:

```python

# bubus/service.py L20-L29

current_handler_id = _current_handler_id_context.get()
if current_handler_id is not None and inside_handler_context.get():
    current_event = _current_event_context.get()
    if (current_event is not None and
        current_handler_id in current_event.event_results and
        event.event_id != current_event.event_id):
        current_event.event_results[current_handler_id].event_children.append(event)

```

This aggregation allows the `event_children` property to return a complete list of descendants without requiring recursive traversal at query time.

## Visualizing the Event Tree

The logging utilities in [`bubus/logging.py`](https://github.com/browser-use/bubus/blob/main/bubus/logging.py) leverage the parent-child metadata to render hierarchical event views. The system builds a parent-to-children mapping using `event_parent_id`:

```python

# bubus/logging.py L71-L76

parent_to_children = defaultdict(list)
for event in eventbus.event_history.values():
    parent_to_children[event.event_parent_id].append(event)

```

This mapping enables tree-structured logging output that reflects the actual execution flow of dispatched events.

## Practical Examples

### Simple Parent-Child Dispatch Inside a Handler

```python
from bubus import EventBus, BaseEvent, Field

class ParentEvent(BaseEvent):
    data: str = Field(default="parent")

class ChildEvent(BaseEvent):
    data: str = Field(default="child")

bus = EventBus(name="demo")

@bus.on(ParentEvent)
def parent_handler(event: ParentEvent):
    # Dispatch a child event while handling the parent

    bus.dispatch(ChildEvent())

# Dispatch the root event

root = bus.dispatch(ParentEvent())

# Await completion

await root

# Inspect the tree

print(f"Parent ID: {root.event_id}")
print(f"Child's recorded parent ID: {root.event_children[0].event_parent_id}")

# → Child's recorded parent ID equals root.event_id

```

### Accessing the Full Subtree from a Parent Event

```python

# After the above dispatch has completed

all_descendants = root.event_children      # direct children

deep_descendants = root.event_children    # property already aggregates grandchildren, etc.

for child in deep_descendants:
    print(f"Child {child.event_type}#{child.event_id[-4:]} – parent {child.event_parent_id[-4:]}")

```

### Using expect to Wait for a Specific Child

```python
@bus.on(ParentEvent)
def fire_child(event: ParentEvent):
    bus.dispatch(ChildEvent())

# Dispatch parent and simultaneously await the child

parent = bus.dispatch(ParentEvent())
child = await bus.expect(ChildEvent)   # blocks until a ChildEvent appears

assert child.event_parent_id == parent.event_id

```

## Summary

- **Automatic Metadata**: Bubus tracks parent-child relationships through the `event_parent_id` field and `event_children` property on every `BaseEvent`.
- **Context-Aware Dispatch**: The `EventBus.dispatch()` method in [`bubus/service.py`](https://github.com/browser-use/bubus/blob/main/bubus/service.py) uses context variables (`_current_event_context`) to automatically assign parent IDs when events are dispatched from within handlers.
- **Real-Time Aggregation**: Child events are appended to `EventResult.event_children` during dispatch, allowing the `event_children` property to return a flat list of all descendants without recursive queries.
- **Visualization Support**: The logging utilities in [`bubus/logging.py`](https://github.com/browser-use/bubus/blob/main/bubus/logging.py) leverage `event_parent_id` to build parent-to-children maps for hierarchical event display.

## Frequently Asked Questions

### Do I need to manually set event_parent_id when dispatching events?

No. Bubus automatically assigns the `event_parent_id` using context variables when an event is dispatched from within a handler. You only need to set it manually if you want to override the default parent relationship or create artificial tree structures.

### How do I access all descendants of an event, not just direct children?

Use the `event_children` property on any `BaseEvent` instance. This property aggregates the `event_children` lists from every `EventResult` attached to the event, returning a flat list that includes all descendants dispatched during the event's processing.

### What happens if an event is dispatched outside of any handler?

If an event is dispatched outside a handler context, `_current_event_context.get()` returns `None`, so the `event_parent_id` remains `None`. This marks the event as a root node in the event tree, which is the expected behavior for top-level application events.

### Where is the parent-child tracking logic implemented?

The core logic resides in [`bubus/service.py`](https://github.com/browser-use/bubus/blob/main/bubus/service.py) within the `EventBus.dispatch()` method, which manages context variables and assigns parent IDs. The data structures are defined in [`bubus/models.py`](https://github.com/browser-use/bubus/blob/main/bubus/models.py) (`BaseEvent` class), and visualization helpers are located in [`bubus/logging.py`](https://github.com/browser-use/bubus/blob/main/bubus/logging.py).