Skip to content

arch(events): workflow_completed and workflow_cancelled are published on two transports under one name #17676

Open

Description

@mrveiss

What

Two workflow events are published twice, on two transports, under the same name:

Name Event bus Raw session socket
workflow_completed api/workflow.py:836 publish_event(f"workflow:{id}", ...) services/workflow_automation/executor.py:725 send_message({"type": ...})
workflow_cancelled api/workflow.py:984 services/workflow_automation/executor.py:801

Found while building the per-transport contract guard for #17643. The two producers' vocabularies are otherwise disjoint — these two are the entire overlap:

bus only   workflow_approval  workflow_approval_required  workflow_step_started
           workflow_step_completed  workflow_failed
both       workflow_completed  workflow_cancelled
socket     automation_control_refused  start_workflow  step_confirmation_required
only       step_failed  step_rejected_by_judge  workflow_paused
           workflow_plan_presented  workflow_resumed

Why it matters

A name shared across transports cannot be reasoned about from the consumer side. #17643's original acceptance criterion asked for a guard that the frontend's event names are a subset of "the backend's published set" — for these two names a union of the producers cannot say which one a given consumer is reading, and for the other thirteen it certifies names that can never reach that consumer. The overlap is why the union framing is wrong rather than merely imprecise.

It is also duplicate publishing: a completing workflow emits a completion twice, by two mechanisms, with different payloads (the bus carries total_steps and execution_time; the socket carries the executor's own shape). Any consumer reading both would double-count.

Current state

Pinned, not fixed. repo_tests/workflow_event_contract_17643_test.py::test_the_shared_names_are_exactly_the_two_we_know_about asserts the overlap is exactly these two, so a third shared name fails and forces a decision rather than quietly widening an overlap nobody chose.

Acceptance criteria

  • A recorded decision on whether a workflow lifecycle event has one transport or two — consolidate, or document why both exist, in EVENT_STATE_DOCTRINE.md, which currently says nothing about either path
  • If consolidated: one producer per lifecycle event, with the consumers moved to it and the redundant publish removed
  • If kept: the two names are distinguished so one string does not mean two things on two sockets
  • The overlap pin in workflow_event_contract_17643_test.py updated to match the decision, not deleted

Refs #17643

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions