UAT: EventBusBridge._STATUS_EVENT_TYPES and _ARTIFACT_EVENT_TYPES use enum member names instead of values — SSE translation never matches #3942

Open
opened 2026-04-06 07:39:58 +00:00 by freemo · 0 comments
Owner

Summary

EventBusBridge._STATUS_EVENT_TYPES and _ARTIFACT_EVENT_TYPES contain enum member names (e.g., "PLAN_CREATED") but the comparison in _on_domain_event() uses the enum value (e.g., "plan.created"). This means the event type translation from domain events to SSE events never matches — all events fall through to the else branch and are passed through with their raw string type instead of being translated to TaskStatusUpdateEvent or TaskArtifactUpdateEvent.

What Was Tested

  • File: src/cleveragents/a2a/events.pyEventBusBridge._STATUS_EVENT_TYPES, _ARTIFACT_EVENT_TYPES, and _on_domain_event()

Expected Behavior (from spec)

Per docs/specification.md §Streaming Architecture:

When a PLAN_CREATED domain event is emitted, the EventBusBridge should translate it to a TaskStatusUpdateEvent for SSE streaming. When a CHECKPOINT_RESTORED event is emitted, it should become a TaskArtifactUpdateEvent.

Actual Behavior

from cleveragents.a2a.events import EventBusBridge
from cleveragents.infrastructure.events.types import EventType
from cleveragents.infrastructure.events.models import DomainEvent

# The sets contain enum NAMES (uppercase)
print(EventBusBridge._STATUS_EVENT_TYPES)
# frozenset({'PLAN_CANCELLED', 'PLAN_ERRORED', 'PLAN_APPLIED', 'PLAN_PHASE_CHANGED', 'PLAN_CREATED', 'PLAN_STATE_CHANGED'})

# But the comparison uses the enum VALUE (lowercase dot-separated)
domain_event = DomainEvent(event_type=EventType.PLAN_CREATED)
type_str = domain_event.event_type.value  # "plan.created"
print(type_str in EventBusBridge._STATUS_EVENT_TYPES)
# False — "plan.created" is NOT in {"PLAN_CREATED", ...}

Root Cause

In src/cleveragents/a2a/events.py:

class EventBusBridge:
    # These contain ENUM NAMES (e.g., "PLAN_CREATED")
    _STATUS_EVENT_TYPES: frozenset[str] = frozenset(
        {
            "PLAN_CREATED",       # ← enum name
            "PLAN_PHASE_CHANGED", # ← enum name
            ...
        }
    )
    
    def _on_domain_event(self, domain_event: Any) -> None:
        # But this extracts the ENUM VALUE (e.g., "plan.created")
        type_str = (
            event_type_name.value  # ← "plan.created"
            if hasattr(event_type_name, "value")
            else str(event_type_name)
        )
        
        if type_str in self._STATUS_EVENT_TYPES:  # "plan.created" in {"PLAN_CREATED", ...} → False!
            sse_type = TASK_STATUS_UPDATE

The sets should contain enum values (e.g., "plan.created") to match what event_type_name.value returns.

Impact

  • All domain events that should become TaskStatusUpdateEvent instead pass through with their raw value (e.g., "plan.created") as the SSE event type
  • All domain events that should become TaskArtifactUpdateEvent similarly pass through unchanged
  • SSE clients receive non-standard event types instead of the spec-defined TaskStatusUpdateEvent and TaskArtifactUpdateEvent
  • This is a companion bug to #3940 (EventBusBridge.start() incompatible subscribe signature)

Steps to Reproduce

from cleveragents.a2a.events import EventBusBridge, A2aEventQueue, TASK_STATUS_UPDATE

# Use a mock bus that accepts single-argument subscribe (to bypass bug #3940)
class MockBus:
    def __init__(self):
        self.callback = None
    def subscribe(self, callback):
        self.callback = callback

queue = A2aEventQueue()
mock_bus = MockBus()
bridge = EventBusBridge(mock_bus, queue)
bridge.start()

# Simulate a PLAN_CREATED domain event
class FakeDomainEvent:
    class event_type:
        value = "plan.created"  # This is what EventType.PLAN_CREATED.value returns
    plan_id = "plan-001"
    details = {}

mock_bus.callback(FakeDomainEvent())
events = queue.get_events()
print(events[-1].event_type)  # "plan.created" — NOT "TaskStatusUpdateEvent"!
print(events[-1].event_type == TASK_STATUS_UPDATE)  # False — WRONG!

Fix

Change the frozensets to use enum values instead of names:

_STATUS_EVENT_TYPES: frozenset[str] = frozenset(
    {
        "plan.created",       # EventType.PLAN_CREATED.value
        "plan.phase_changed", # EventType.PLAN_PHASE_CHANGED.value
        "plan.state_changed", # EventType.PLAN_STATE_CHANGED.value
        "plan.applied",       # EventType.PLAN_APPLIED.value
        "plan.cancelled",     # EventType.PLAN_CANCELLED.value
        "plan.errored",       # EventType.PLAN_ERRORED.value
    }
)

_ARTIFACT_EVENT_TYPES: frozenset[str] = frozenset(
    {
        "checkpoint.restored",  # EventType.CHECKPOINT_RESTORED.value
    }
)

Or alternatively, use the enum members directly:

from cleveragents.infrastructure.events.types import EventType

_STATUS_EVENT_TYPES: frozenset[str] = frozenset(
    e.value for e in [
        EventType.PLAN_CREATED,
        EventType.PLAN_PHASE_CHANGED,
        EventType.PLAN_STATE_CHANGED,
        EventType.PLAN_APPLIED,
        EventType.PLAN_CANCELLED,
        EventType.PLAN_ERRORED,
    ]
)
  • Companion bug: #3940 (EventBusBridge.start() incompatible subscribe signature)
  • Parent Epic: #397 (Server & Autonomy Infrastructure)

Automated by CleverAgents Bot
Supervisor: UAT Testing | Agent: ca-uat-tester

## Summary `EventBusBridge._STATUS_EVENT_TYPES` and `_ARTIFACT_EVENT_TYPES` contain **enum member names** (e.g., `"PLAN_CREATED"`) but the comparison in `_on_domain_event()` uses the **enum value** (e.g., `"plan.created"`). This means the event type translation from domain events to SSE events **never matches** — all events fall through to the `else` branch and are passed through with their raw string type instead of being translated to `TaskStatusUpdateEvent` or `TaskArtifactUpdateEvent`. ## What Was Tested - File: `src/cleveragents/a2a/events.py` — `EventBusBridge._STATUS_EVENT_TYPES`, `_ARTIFACT_EVENT_TYPES`, and `_on_domain_event()` ## Expected Behavior (from spec) Per `docs/specification.md` §Streaming Architecture: When a `PLAN_CREATED` domain event is emitted, the `EventBusBridge` should translate it to a `TaskStatusUpdateEvent` for SSE streaming. When a `CHECKPOINT_RESTORED` event is emitted, it should become a `TaskArtifactUpdateEvent`. ## Actual Behavior ```python from cleveragents.a2a.events import EventBusBridge from cleveragents.infrastructure.events.types import EventType from cleveragents.infrastructure.events.models import DomainEvent # The sets contain enum NAMES (uppercase) print(EventBusBridge._STATUS_EVENT_TYPES) # frozenset({'PLAN_CANCELLED', 'PLAN_ERRORED', 'PLAN_APPLIED', 'PLAN_PHASE_CHANGED', 'PLAN_CREATED', 'PLAN_STATE_CHANGED'}) # But the comparison uses the enum VALUE (lowercase dot-separated) domain_event = DomainEvent(event_type=EventType.PLAN_CREATED) type_str = domain_event.event_type.value # "plan.created" print(type_str in EventBusBridge._STATUS_EVENT_TYPES) # False — "plan.created" is NOT in {"PLAN_CREATED", ...} ``` ## Root Cause In `src/cleveragents/a2a/events.py`: ```python class EventBusBridge: # These contain ENUM NAMES (e.g., "PLAN_CREATED") _STATUS_EVENT_TYPES: frozenset[str] = frozenset( { "PLAN_CREATED", # ← enum name "PLAN_PHASE_CHANGED", # ← enum name ... } ) def _on_domain_event(self, domain_event: Any) -> None: # But this extracts the ENUM VALUE (e.g., "plan.created") type_str = ( event_type_name.value # ← "plan.created" if hasattr(event_type_name, "value") else str(event_type_name) ) if type_str in self._STATUS_EVENT_TYPES: # "plan.created" in {"PLAN_CREATED", ...} → False! sse_type = TASK_STATUS_UPDATE ``` The sets should contain enum **values** (e.g., `"plan.created"`) to match what `event_type_name.value` returns. ## Impact - All domain events that should become `TaskStatusUpdateEvent` instead pass through with their raw value (e.g., `"plan.created"`) as the SSE event type - All domain events that should become `TaskArtifactUpdateEvent` similarly pass through unchanged - SSE clients receive non-standard event types instead of the spec-defined `TaskStatusUpdateEvent` and `TaskArtifactUpdateEvent` - This is a companion bug to #3940 (EventBusBridge.start() incompatible subscribe signature) ## Steps to Reproduce ```python from cleveragents.a2a.events import EventBusBridge, A2aEventQueue, TASK_STATUS_UPDATE # Use a mock bus that accepts single-argument subscribe (to bypass bug #3940) class MockBus: def __init__(self): self.callback = None def subscribe(self, callback): self.callback = callback queue = A2aEventQueue() mock_bus = MockBus() bridge = EventBusBridge(mock_bus, queue) bridge.start() # Simulate a PLAN_CREATED domain event class FakeDomainEvent: class event_type: value = "plan.created" # This is what EventType.PLAN_CREATED.value returns plan_id = "plan-001" details = {} mock_bus.callback(FakeDomainEvent()) events = queue.get_events() print(events[-1].event_type) # "plan.created" — NOT "TaskStatusUpdateEvent"! print(events[-1].event_type == TASK_STATUS_UPDATE) # False — WRONG! ``` ## Fix Change the frozensets to use enum **values** instead of names: ```python _STATUS_EVENT_TYPES: frozenset[str] = frozenset( { "plan.created", # EventType.PLAN_CREATED.value "plan.phase_changed", # EventType.PLAN_PHASE_CHANGED.value "plan.state_changed", # EventType.PLAN_STATE_CHANGED.value "plan.applied", # EventType.PLAN_APPLIED.value "plan.cancelled", # EventType.PLAN_CANCELLED.value "plan.errored", # EventType.PLAN_ERRORED.value } ) _ARTIFACT_EVENT_TYPES: frozenset[str] = frozenset( { "checkpoint.restored", # EventType.CHECKPOINT_RESTORED.value } ) ``` Or alternatively, use the enum members directly: ```python from cleveragents.infrastructure.events.types import EventType _STATUS_EVENT_TYPES: frozenset[str] = frozenset( e.value for e in [ EventType.PLAN_CREATED, EventType.PLAN_PHASE_CHANGED, EventType.PLAN_STATE_CHANGED, EventType.PLAN_APPLIED, EventType.PLAN_CANCELLED, EventType.PLAN_ERRORED, ] ) ``` ## Related Issues - Companion bug: #3940 (EventBusBridge.start() incompatible subscribe signature) - Parent Epic: #397 (Server & Autonomy Infrastructure) --- **Automated by CleverAgents Bot** Supervisor: UAT Testing | Agent: ca-uat-tester
HAL9000 added this to the v3.5.0 milestone 2026-04-09 15:18:26 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
cleveragents/cleveragents-core#3942
No description provided.