Skip to content

feat(OMN-10837): route tool-content emission through node_emit_daemon - #1630

Merged
jonahgabriel merged 7 commits into
mainfrom
jonah/omn-10837-route-through-node-emit-daemon
May 22, 2026
Merged

jonahgabriel merged 7 commits into
mainfrom
jonah/omn-10837-route-through-node-emit-daemon

Conversation

@jonahgabriel

@jonahgabriel jonahgabriel commented May 22, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Replaces legacy EventBusKafka embedded-publisher path in _emit_tool_content() with synchronous emit_event() call routed through node_emit_daemon Unix socket client
  • Adds tool.content to SUPPORTED_EVENT_TYPES in emit_client_wrapper.py
  • Registers tool.content → onex.cmd.omniintelligence.tool-content.v1 fan-out in omniclaude.yaml
  • Updates test_cli_emit.py to mock the daemon path instead of the removed EventBusKafka path

The daemon provides circuit-breaker + exponential backoff + disk-spool durability, eliminating the crash-loop that occurred when Redpanda restarted.

Test plan

  • uv run pytest tests/hooks/test_cli_emit.py — 58/58 passed
  • uv run ruff format && uv run ruff check --fix — clean
  • pre-commit run on staged files — all passed
  • Push hooks (mypy, pyright, bandit, ONEX validation) — all passed

Evidence-Ticket: OMN-10837

Summary by CodeRabbit

  • New Features

    • Added verified dispatch orchestrator skill for enhanced capability routing.
    • Introduced support for tool.content event type in the event system.
  • Refactor

    • Tool content emission now uses a synchronous daemon-based publisher instead of direct asynchronous Kafka.

Review Change Stack

Evidence-Ticket: OMN-10837
Evidence-Source: 151d4e88656ef645b5dd11ffedafbf492cbdbe0a

Replaces the legacy embedded EventBusKafka path (direct Kafka connection,
no retry, crash-loop on Redpanda restart) with the node_emit_daemon Unix
socket client, which provides circuit-breaker, exponential backoff, and
disk-spool durability.

Changes:
- cli_emit.py: _emit_tool_content() is now synchronous, calls emit_event()
  from emit_client_wrapper instead of creating an EventBusKafka instance
- emit_client_wrapper.py: add "tool.content" to SUPPORTED_EVENT_TYPES
- omniclaude.yaml: register tool.content → onex.cmd.omniintelligence.tool-content.v1
- test_cli_emit.py: update test to mock daemon path instead of legacy EventBusKafka

Evidence-Ticket: OMN-10837
@jonahgabriel
jonahgabriel enabled auto-merge May 22, 2026 04:58
@coderabbitai

coderabbitai Bot commented May 22, 2026 •

Copy link
Copy Markdown

Warning

Rate limit exceeded

@jonahgabriel has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 3 minutes and 32 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cec67c06-b137-4fff-97d8-f5cb33e1c76f

📥 Commits

Reviewing files that changed from the base of the PR and between 0659036 and 5a324eb.

📒 Files selected for processing (6)
  • .github/workflows/security-scan.yml
  • deployment/Dockerfile
  • src/omniclaude/hooks/event_registry.py
  • tests/hooks/test_cli_emit.py
  • tests/hooks/test_emit_client_wrapper.py
  • tests/hooks/test_event_registry.py
📝 Walkthrough

Walkthrough

This PR introduces two independent features: (1) migrates tool-content event emission from embedded async Kafka publishing to synchronous daemon-based routing with updated event registry and test coverage, and (2) adds a new verified_dispatch orchestrator node capability with contract schema, dependency injection, and error-handling policy.

Changes

Tool Content Event Daemon Routing

Layer / File(s) Summary
Event type registration and schema definition
plugins/onex/hooks/lib/emit_client_wrapper.py, plugins/onex/lib/event_registry/omniclaude.yaml
Add "tool.content" to SUPPORTED_EVENT_TYPES and register its event definition with fan-out topic onex.cmd.omniintelligence.tool-content.v1, partition key session_id, and required fields [tool_name_raw, session_id].
CLI emission refactor to daemon-based publisher
src/omniclaude/hooks/cli_emit.py
Refactor _emit_tool_content from async Kafka-bus publisher (with config/bootstrap/close per call) to synchronous daemon publisher that dynamically imports emit_event from plugin hooks; remove Kafka-related imports and update cmd_tool_content to invoke the function directly with result-based logging instead of run_with_timeout wrapping.
Daemon emission test coverage
tests/hooks/test_cli_emit.py
Replace Kafka-specific mock stubs with new test_daemon_emission_payload_structure that patches emit_event and verifies tool.content event payload structure including tool name, session id, content fields, success status, timestamp, and correlation id.

Verified Dispatch Orchestrator Node

Layer / File(s) Summary
Orchestrator contract definition
src/omniclaude/nodes/node_skill_verified_dispatch_orchestrator/contract.yaml
Define orchestrator contract with ModelSkillRequest / ModelSkillResult shared types, single skill_requested I/O operation mapped to handle_skill_requested, event-bus subscriptions (onex.cmd.omniclaude.verified_dispatch.v1) and publish topics (success/failure), capability metadata for skill.verified_dispatch, retry policy with max_retries=2 and exponential backoff on dispatch/timeout errors, and handler dependencies.
Orchestrator node class and package structure
src/omniclaude/nodes/node_skill_verified_dispatch_orchestrator/node.py, src/omniclaude/nodes/node_skill_verified_dispatch_orchestrator/__init__.py
Implement NodeSkillVerifiedDispatchOrchestrator as a thin NodeOrchestrator subclass that accepts ModelONEXContainer via constructor injection and delegates dispatch behavior to shared handler; define package-level exports via __all__ and module-level re-export.

Sequence Diagram(s)

Tool-content emission now flows through the daemon instead of embedded Kafka:

sequenceDiagram
    participant CLI as CLI Command<br/>cmd_tool_content
    participant Emitter as _emit_tool_content<br/>sync publisher
    participant PluginLoader as Plugin System<br/>emit_event loader
    participant Daemon as Event Daemon<br/>socket/IPC
    CLI->>Emitter: call _emit_tool_content(content)
    Emitter->>PluginLoader: dynamically load emit_event<br/>from CLAUDE_PLUGIN_ROOT
    Emitter->>Daemon: emit_event("tool.content",<br/>content JSON payload)
    Daemon->>Daemon: validate, route to<br/>onex.cmd.omniintelligence.tool-content.v1
    Daemon-->>Emitter: return bool success
    Emitter-->>CLI: return ModelEventPublishResult
    CLI->>CLI: log success/failure
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A daemon takes the message now,
No Kafka queue to tend somehow,
And orchestrators dance with grace,
New verified dispatches take their place!
Events flow through socket's door, ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 71.43% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title directly and clearly describes the main change: routing tool-content emission through node_emit_daemon instead of the legacy EventBusKafka path.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jonah/omn-10837-route-through-node-emit-daemon

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

@github-actions

github-actions Bot commented May 22, 2026 •

Copy link
Copy Markdown
Contributor

⚠️ Hostile Reviewer — DEGRADED (informational)

Blocking findings (critical/error): 0
Total findings: 0
Models succeeded: none

Note: All reviewer models failed or were unavailable. Degraded results are informational during the pilot phase (OMN-8524) and do not block merge. Error: cli_review exited non-zero (all models failed or binary error)


Gate semantics (pilot phase)

Verdict Meaning Blocks merge?
passed No critical/error findings No
blocked CRITICAL or ERROR findings found Yes
degraded All models unavailable (infra) No (pilot)

Powered by omniintelligence.review_pairing.cli_review — node-based adversarial review via HandlerLlmCliSubprocess (OMN-8524)

Comment thread tests/hooks/test_cli_emit.py Fixed
Comment thread tests/hooks/test_cli_emit.py Fixed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
tests/hooks/test_cli_emit.py (1)

765-847: 💤 Low value

Test contains unused mock setup that could be simplified.

The fake_emit_event function (lines 775-779) and its patch at line 782 are never exercised because patched_emit_tool_content doesn't call emit_event — it directly captures the content and returns a successful result. The patch.dict("sys.modules", {}) at line 785 is also a no-op since an empty dict doesn't modify anything.

The test correctly validates the payload structure, but could be simplified:

♻️ Simplified test structure
     def test_daemon_emission_payload_structure(self, runner: CliRunner) -> None:
         """Verify daemon emit_event is called with correct event type and payload.

         OMN-10837: tool-content now routes through node_emit_daemon instead of
         a direct EventBusKafka connection. This test mocks emit_event from
         emit_client_wrapper and verifies the payload structure is correct.
         """

         captured_calls: list[tuple[str, dict]] = []

-        def fake_emit_event(
-            event_type: str, payload: dict, timeout_ms: int = 3000
-        ) -> bool:
-            captured_calls.append((event_type, payload))
-            return True
+        def patched_emit_tool_content(content):
+            import json as _json
+
+            payload = _json.loads(content.model_dump_json())
+            captured_calls.append(("tool.content", payload))
+            from omniclaude.hooks.models import ModelEventPublishResult
+            from omniclaude.hooks.topics import TopicBase, build_topic
+
+            return ModelEventPublishResult(
+                success=True,
+                topic=build_topic(TopicBase.TOOL_CONTENT),
+                partition=None,
+                offset=None,
+            )

+        import omniclaude.hooks.cli_emit as cli_emit_mod
+
-        with patch(
-            "omniclaude.hooks.cli_emit.emit_event", fake_emit_event, create=True
-        ):
-            # Patch the module-level import inside _emit_tool_content
-            with patch.dict("sys.modules", {}):
-                import omniclaude.hooks.cli_emit as cli_emit_mod
-
-                original_emit = None
-
-                def patched_emit_tool_content(content):
-                    import json as _json
-
-                    payload = _json.loads(content.model_dump_json())
-                    captured_calls.append(("tool.content", payload))
-                    from omniclaude.hooks.models import ModelEventPublishResult
-                    from omniclaude.hooks.topics import TopicBase, build_topic
-
-                    return ModelEventPublishResult(
-                        success=True,
-                        topic=build_topic(TopicBase.TOOL_CONTENT),
-                        partition=None,
-                        offset=None,
-                    )
-
-                with patch.object(
-                    cli_emit_mod, "_emit_tool_content", patched_emit_tool_content
-                ):
-                    result = runner.invoke(...)
+        with patch.object(cli_emit_mod, "_emit_tool_content", patched_emit_tool_content):
+            result = runner.invoke(...)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/hooks/test_cli_emit.py` around lines 765 - 847, The test defines an
unused fake_emit_event and applies a patch for
"omniclaude.hooks.cli_emit.emit_event" and also wraps the import in
patch.dict("sys.modules", {}) even though patched_emit_tool_content never calls
emit_event; remove the fake_emit_event function, the with
patch("omniclaude.hooks.cli_emit.emit_event", ...) block, and the with
patch.dict("sys.modules", {}) wrapper, instead import omniclaude.hooks.cli_emit
directly and only patch cli_emit_mod._emit_tool_content
(patched_emit_tool_content) and assert captured_calls as before.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/omniclaude/nodes/node_skill_verified_dispatch_orchestrator/contract.yaml`:
- Around line 59-70: Update all event-bus identifiers to use the hyphenated
skill-id "verified-dispatch": replace topic
("onex.cmd.omniclaude.verified_dispatch.v1") with
"onex.cmd.omniclaude.verified-dispatch.v1", consumer_group
("omniclaude.skill.verified_dispatch") with
"omniclaude.skill.verified-dispatch", success_topic
("onex.evt.omniclaude.verified_dispatch-completed.v1") and failure_topic
("onex.evt.omniclaude.verified_dispatch-failed.v1") with their hyphenated
equivalents, and update the canonical lists subscribe_topics and publish_topics
to use "onex.cmd.omniclaude.verified-dispatch.v1",
"onex.evt.omniclaude.verified-dispatch-completed.v1", and
"onex.evt.omniclaude.verified-dispatch-failed.v1" respectively.

---

Nitpick comments:
In `@tests/hooks/test_cli_emit.py`:
- Around line 765-847: The test defines an unused fake_emit_event and applies a
patch for "omniclaude.hooks.cli_emit.emit_event" and also wraps the import in
patch.dict("sys.modules", {}) even though patched_emit_tool_content never calls
emit_event; remove the fake_emit_event function, the with
patch("omniclaude.hooks.cli_emit.emit_event", ...) block, and the with
patch.dict("sys.modules", {}) wrapper, instead import omniclaude.hooks.cli_emit
directly and only patch cli_emit_mod._emit_tool_content
(patched_emit_tool_content) and assert captured_calls as before.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fdf2e7ae-ff14-43ce-82f5-f079e73e032d

📥 Commits

Reviewing files that changed from the base of the PR and between 4385b54 and 0659036.

📒 Files selected for processing (7)
  • plugins/onex/hooks/lib/emit_client_wrapper.py
  • plugins/onex/lib/event_registry/omniclaude.yaml
  • src/omniclaude/hooks/cli_emit.py
  • src/omniclaude/nodes/node_skill_verified_dispatch_orchestrator/__init__.py
  • src/omniclaude/nodes/node_skill_verified_dispatch_orchestrator/contract.yaml
  • src/omniclaude/nodes/node_skill_verified_dispatch_orchestrator/node.py
  • tests/hooks/test_cli_emit.py

…ming

- Add "tool.content" EventRegistration to event_registry.py so it matches
  SUPPORTED_EVENT_TYPES in emit_client_wrapper (test_supported_event_types_match_event_registry)
- Add "tool.content" to expected_types in test_emit_client_wrapper.py
- Simplify test_daemon_emission_payload_structure in test_cli_emit.py:
  remove unused original_emit variable and nested sys.modules patch
  (fixes CodeQL unused-variable and mixed-import-style warnings)
- Fix contract.yaml event-bus identifiers: verified_dispatch -> verified-dispatch
  in topic strings and consumer group (CodeRabbit hyphen convention)
Comment thread tests/hooks/test_cli_emit.py Fixed
auto-merge was automatically disabled May 22, 2026 09:01

Pull request was closed

@jonahgabriel jonahgabriel reopened this May 22, 2026
@jonahgabriel
jonahgabriel enabled auto-merge May 22, 2026 09:01
@jonahgabriel
jonahgabriel added this pull request to the merge queue May 22, 2026
@jonahgabriel
jonahgabriel removed this pull request from the merge queue due to a manual request May 22, 2026
@github-actions
github-actions Bot enabled auto-merge May 22, 2026 12:27
@github-actions
github-actions Bot added this pull request to the merge queue May 22, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to no response for status checks May 22, 2026
@jonahgabriel
jonahgabriel added this pull request to the merge queue May 22, 2026
Merged via the queue into main with commit b0b4fb6 May 22, 2026
94 checks passed
@jonahgabriel
jonahgabriel deleted the jonah/omn-10837-route-through-node-emit-daemon branch May 22, 2026 14:29
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.

2 participants