When a handler emits another event, AbxBus automatically records lineage so you can understand call chains instead of guessing what triggered what. Repository example files:Documentation Index
Fetch the complete documentation index at: https://abxbus.archivebox.io/llms.txt
Use this file to discover all available pages before exploring further.
examples/parent_child_tracking.pyabxbus-ts/examples/parent_child_tracking.tsexamples/log_tree_demo.pyabxbus-ts/examples/log_tree_demo.ts
What gets tracked
event_parent_id: points from child -> parent eventevent_children: aggregated list of child events emitted withevent.emit(...)during handler executionevent_emitted_by_handler_id: which specific handler emitted the child
Which emit style should I use?
Most handler code should useawait event.emit(ChildEvent(...)) in Python or await event.emit(ChildEvent(...)).done() in TypeScript. That is the “owned child work” style: the child is linked to the parent, runs immediately from the caller’s point of view, and the parent cannot complete until the child completes.
Use the other styles only when you explicitly want linked background work or a fully detached top-level event.
| Style | event_parent_id | event_blocks_parent_completion | Blocks current handler? | Effect |
|---|---|---|---|---|
await event.emit(ChildEvent(...))await event.emit(ChildEvent(...)).done() | Parent event id | True / true | Yes | Linked child work. The child can queue-jump, and parent completion waits for the child. |
event.emit(ChildEvent(...)) without awaiting | Parent event id | False / false | No | Linked background child. It appears in ancestry/tree logs, but the parent can complete before the child finishes. |
await bus.emit(TopLevelEvent(...))await bus.emit(TopLevelEvent(...)).done() | None / null | False / false because there is no linked parent to block | Yes | Detached top-level event that the current handler waits for naturally because it is awaited. It is not a child in hierarchy tracking. |
bus.emit(TopLevelEvent(...)) without awaiting | None / null | False / false | No | True background event. It is queued for later processing and has no retained relationship to the event that emitted it. |
event.emit(...) when you want parent-child lineage, and add await when that child work is part of the parent’s completion. Use bus.emit(...) when the event should be treated as a separate top-level event.
Works across forwarded buses too
Parent-child lineage is preserved even when the parent event has been forwarded between buses. If a forwarded event is handled on another bus and that handler emits a child:- the child still gets
event_parent_id = <forwarded parent event_id> - the child is linked under the emitting handler’s
event_children - forwarding that child onward keeps the same lineage metadata
event.emit(...) in handlers so the runtime can attach ancestry and ownership correctly.
See also: Forwarding Between Buses
Queue-jumped vs normally queued linked children
Lineage tracking works in both execution styles:- Queue-jumped child events:
- emitted inside a handler and immediately awaited (
await child/await child.done()) - child may execute right away (RPC-style), gets normal parent linkage metadata, and sets
event_blocks_parent_completion=True
- emitted inside a handler and immediately awaited (
- Normally queued child events:
- emitted inside a handler but not immediately awaited
- child runs later via normal queue scheduling, keeps the same
event_parent_idancestry link, and leavesevent_blocks_parent_completion=False
event.emit(...) records lineage. Awaiting the emitted event decides whether that lineage also blocks parent completion.
See Immediate Execution (RPC-style) for queue-jump behavior details.
Full example: checkout -> reserve/charge/receipt (+ fraud grandchild)
- Python
- TypeScript
Example tree output
Captured from running the Python example above withuv run (IDs/timestamps vary run-to-run):
Why this helps in practice
- Debugging: quickly see causality chains instead of inspecting raw logs line-by-line.
- Reliability: timeout/cancellation behavior can be reasoned about by ancestry.
- Querying: combine lineage with
find(..., child_of=...)to isolate event families.