Skip to content

feat(mixins): remove listener management from MixinEventBus [OMN-1747] - #456

Merged
jonahgabriel merged 2 commits into
mainfrom
jonah/omn-1747-cleanup-remove-listener-management-from-mixineventbus
Jan 31, 2026
Merged

jonahgabriel merged 2 commits into
mainfrom
jonah/omn-1747-cleanup-remove-listener-management-from-mixineventbus

Conversation

@jonahgabriel

@jonahgabriel jonahgabriel commented Jan 31, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Remove listener/consumer lifecycle code from omnibase_core to enforce the architectural invariant: "infra owns Kafka plumbing, core provides contracts + publish-only abstractions."

This resolves the competing subscription systems between:

  • omnibase_core: MixinEventBus listener management (REMOVED)
  • omnibase_infra: EventBusSubcontractWiring (KEPT - canonical approach)

Changes

Removed Files (5 files, -4,368 lines)

File Reason
mixin_event_listener.py 100% listener code
model_event_bus_listener_handle.py Listener handle model
protocol_event_bus_listener.py Orphaned protocol
test_mixin_event_listener.py 30 listener tests
test_model_event_bus_listener_handle.py 62 listener tests

Refactored

  • mixin_event_bus.py: 40% reduction (1977 → 1200 lines)
    • ❌ Removed: start_event_listener, stop_event_listener, _event_listener_loop
    • ❌ Removed: get_event_patterns, get_completion_event_type, bind_contract_path
    • ❌ Removed: _create_event_handler, _event_to_input_state, _get_input_state_class
    • ✅ Kept: publish_event, publish_completion_event, apublish_completion_event
    • ✅ Kept: bind_event_bus, bind_registry, bind_node_name

Updated

File Change
model_service_effect.py Removed stop_event_listener() fallback
test_mixin_event_bus.py 25 listener tests removed, 53 publish tests kept
conftest.py Removed dead listener thread cleanup code
mixin_tool_execution.py Updated docstrings
Various __init__.py Updated exports

Added (CI Guard)

File Purpose
scripts/validation/validate-no-listener-apis.py Prevents listener reintroduction
.pre-commit-config.yaml Hook: validate-no-listener-apis

Inventory Evidence

Production references verified:

  • ✅ No production code calls start_event_listener()
  • ✅ model_service_effect.py only had shutdown fallback (now removed)
  • ✅ All consumers of listener APIs were tests (now deleted)

Test Plan

  • mypy passes on modified files
  • 53 publish-related tests pass
  • All pre-commit hooks pass (44 checks)
  • Import smoke tests pass
  • Full CI test suite

Breaking Changes

⚠️ Breaking: MixinEventListener and related APIs removed.

Migration: Use EventBusSubcontractWiring in omnibase_infra for Kafka consumer lifecycle management.

References

  • Linear: OMN-1747
  • Invariant: contract declares subscriptions ⇒ infra wiring runs ⇒ no core listener code needed

Summary by CodeRabbit

  • Refactor

    • Event bus is now publish-only; listener/consumer lifecycle and related public APIs have been removed.
  • Chores

    • Added a pre-commit validation hook to prevent reintroduction of deprecated listener APIs.
  • Tests

    • Removed numerous listener-related unit tests.
  • Documentation

    • Bumped package version to 0.10.0 and updated changelog.

✏️ Tip: You can customize this high-level summary in your review settings.

Remove listener/consumer lifecycle code from omnibase_core to enforce
the architectural invariant: "infra owns Kafka plumbing, core provides
contracts + publish-only abstractions."

## Removed Files
- mixin_event_listener.py (100% listener code)
- model_event_bus_listener_handle.py (listener handle model)
- protocol_event_bus_listener.py (orphaned protocol)
- test_mixin_event_listener.py (30 tests)
- test_model_event_bus_listener_handle.py (62 tests)

## Refactored
- mixin_event_bus.py: 40% reduction (1977→1200 lines)
  - Removed: start_event_listener, stop_event_listener, _event_listener_loop
  - Removed: get_event_patterns, get_completion_event_type, bind_contract_path
  - Removed: _create_event_handler, _event_to_input_state, _get_input_state_class
  - Kept: publish_event, publish_completion_event, apublish_completion_event
  - Kept: bind_event_bus, bind_registry, bind_node_name

## Updated
- model_service_effect.py: Removed stop_event_listener() fallback
- test_mixin_event_bus.py: 25 listener tests removed, 53 publish tests kept
- conftest.py: Removed dead listener thread cleanup code
- mixin_tool_execution.py: Updated docstrings
- Various __init__.py: Updated exports

## Added
- scripts/validation/validate-no-listener-apis.py: CI guard
- .pre-commit-config.yaml: Hook to prevent listener reintroduction

Breaking change: MixinEventListener and related APIs removed.
Use EventBusSubcontractWiring in omnibase_infra for subscriptions.
@coderabbitai

coderabbitai Bot commented Jan 31, 2026 •

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This PR removes event-listener functionality, converting the event bus to publish-only; deletes listener-related classes and tests; adds a pre-commit validation hook and script preventing reintroduction of listener APIs; and bumps version to 0.10.0.

Changes

Cohort / File(s) Summary
Pre-commit validation setup
\.pre-commit-config\.yaml, scripts/validation/validate-no-listener-apis.py
Adds a pre-commit hook and a validation script that scans src/omnibase_core Python files for forbidden listener API patterns (start_event_listener, stop_event_listener, _event_listener_loop, ModelEventBusListenerHandle and imports), with an opt-out header comment.
Listener implementation removals
src/omnibase_core/mixins/mixin_event_listener.py, src/omnibase_core/models/event_bus/model_event_bus_listener_handle.py, src/omnibase_core/protocols/event_bus/protocol_event_bus_listener.py
Removes the MixinEventListener, ModelEventBusListenerHandle model, and ProtocolEventBusListener protocol — deleting subscription/lifecycle logic, thread-handling model, and listener interface.
Event bus refactor (publish-only)
src/omnibase_core/mixins/mixin_event_bus.py, src/omnibase_core/mixins/__init__.py
Refactors MixinEventBus to publish-only: removes subscribe/unsubscribe and listener lifecycle methods/exports, retains publish methods and binding helpers; removes MixinEventListener export.
Public API cleanup
src/omnibase_core/models/event_bus/__init__.py, src/omnibase_core/protocols/event_bus/__init__.py
Removes exports of ModelEventBusListenerHandle and ProtocolEventBusListener from package-level all.
Docs and minor behavior updates
src/omnibase_core/mixins/mixin_tool_execution.py, src/omnibase_core/models/services/model_service_effect.py
Updated docstrings to reflect publish-only semantics and removed fallback to stop_event_listener() in cleanup logic.
Tests and test harness changes
tests/unit/mixins/test_mixin_event_listener.py, tests/unit/models/event_bus/test_model_event_bus_listener_handle.py, tests/unit/mixins/test_mixin_event_bus.py, tests/conftest.py
Deletes listener-focused test suites and many event-listener tests; simplifies conftest.py cleanup by removing thread-joining logic and leaving a direct gc.collect().
Release/version changes
pyproject.toml, src/omnibase_core/__init__.py, CHANGELOG.md
Bumps project and package version to 0.10.0 and documents breaking change: removal of listener APIs and addition of pre-commit guard.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Dev as Developer
    participant Git as Git
    participant Hook as Pre-commit Hook (ONEX Validation)
    participant Script as validate-no-listener-apis.py
    participant FS as Repository Files

    Dev->>Git: git commit
    Git->>Hook: run configured hooks
    Hook->>Script: invoke via poetry
    Script->>FS: scan `src/omnibase_core` python files
    alt forbidden patterns found
        Script->>Hook: return non-zero / report violations
        Hook->>Git: block commit (fail)
        Git->>Dev: show validation failures
    else no violations
        Script->>Hook: return success
        Hook->>Git: allow commit to proceed
        Git->>Dev: commit succeeds
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~55 minutes

Possibly related PRs

Poem

🐰
I thumped my foot where listeners lay,
Now messages hop a publish-way,
A tiny guard checks every line—
No sneaky listeners can entwine. 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main change: removal of listener management from MixinEventBus, with issue reference OMN-1747.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch jonah/omn-1747-cleanup-remove-listener-management-from-mixineventbus

Comment @coderabbitai help to get the list of available commands and usage tips.

Breaking change release for listener management removal.
@jonahgabriel
jonahgabriel force-pushed the jonah/omn-1747-cleanup-remove-listener-management-from-mixineventbus branch from 4fb1a0c to 52720bd Compare January 31, 2026 14:17

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/omnibase_core/mixins/mixin_event_bus.py (1)

1064-1121: ⚠️ Potential issue | 🟠 Major

dispose_event_bus_resources can raise when runtime state was never bound.

_event_bus_runtime_state raises ModelOnexError if the state is missing, which breaks the method’s “idempotent and safe to call multiple times” contract for unbound instances. Consider using _ensure_runtime_state() (or guarding on state presence) to avoid raising during cleanup.

🔧 Suggested fix
-            try:
-                self._event_bus_runtime_state.reset()
-            except (AttributeError, RuntimeError) as e:
+            try:
+                self._ensure_runtime_state().reset()
+            except (ModelOnexError, RuntimeError) as e:
                 cleanup_errors.append(f"Failed to reset runtime state: {e!r}")
                 emit_log_event(
                     LogLevel.ERROR,
                     f"MIXIN_DISPOSE: Failed to reset runtime state: {e!r}",
                     ModelLogData(node_name=self.get_node_name()),
                 )
🧹 Nitpick comments (2)
src/omnibase_core/models/services/model_service_effect.py (1)

115-120: Guard dispose_event_bus_resources with a callable check.
hasattr only verifies presence; a non-callable attribute (e.g., overridden to None) would raise during shutdown. Consider resolving once and checking callable for resilience.

Suggested fix
-        if hasattr(self, "dispose_event_bus_resources"):
-            self.dispose_event_bus_resources()
+        dispose = getattr(self, "dispose_event_bus_resources", None)
+        if callable(dispose):
+            dispose()
scripts/validation/validate-no-listener-apis.py (1)

2-39: Trim the module docstring to 3–6 lines.

It’s currently far longer than the project standard; move the usage/details into argparse help or a README section.

💡 Proposed docstring trim
-"""
-ONEX Listener API Prevention Validation.
-
-OMN-1747: Listener/consumer lifecycle management was removed from omnibase_core.
-This pre-commit hook prevents these APIs from being reintroduced.
-
-For Kafka consumer management, use EventBusSubcontractWiring in omnibase_infra.
-
-Detected Patterns:
-1. def start_event_listener( - Listener start method
-2. def stop_event_listener( - Listener stop method
-3. def _event_listener_loop( - Internal listener loop
-4. class ModelEventBusListenerHandle - Listener handle model
-5. from ... import ... ModelEventBusListenerHandle - Imports of listener handle
-
-Usage:
-    # Check specific files (pre-commit mode)
-    poetry run python scripts/validation/validate-no-listener-apis.py <file1> [file2] ...
-
-    # Check entire src/ directory (default)
-    poetry run python scripts/validation/validate-no-listener-apis.py
-
-    # Check specific directory
-    poetry run python scripts/validation/validate-no-listener-apis.py src/omnibase_core/
-
-    To allow listener APIs in specific files (rare cases), add comment:
-    # listener-api-ok: reason for exception
-
-Rationale:
-    - omnibase_core must remain transport-agnostic (ADR-005)
-    - Listener lifecycle creates tight coupling to Kafka implementation
-    - Consumer management belongs in omnibase_infra via EventBusSubcontractWiring
-    - Core should only define protocols, not implementations
-
-Reference:
-    - OMN-1747: Remove listener management from omnibase_core
-    - docs/architecture/adr/ADR-005-core-infra-dependency-boundary.md
-"""
+"""
+ONEX listener API prevention validator.
+
+OMN-1747 blocks reintroduction of listener lifecycle APIs in omnibase_core.
+Use EventBusSubcontractWiring in omnibase_infra for consumer management.
+See ADR-005 for the core/infra boundary.
+"""

As per coding guidelines: Module docstrings must be concise (3-6 lines max for simple modules); follow PEP 257 with module docstring first, then from __future__ import annotations.

@jonahgabriel
jonahgabriel merged commit ac32763 into main Jan 31, 2026
30 checks passed
@jonahgabriel
jonahgabriel deleted the jonah/omn-1747-cleanup-remove-listener-management-from-mixineventbus branch January 31, 2026 15:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant