Skip to content

fix: share diagnostic event bus across bundle chunks via globalThis#18894

Closed
aiwithabidi wants to merge 1 commit intoopenclaw:mainfrom
aiwithabidi:fix/diagnostic-events-shared-bus
Closed

fix: share diagnostic event bus across bundle chunks via globalThis#18894
aiwithabidi wants to merge 1 commit intoopenclaw:mainfrom
aiwithabidi:fix/diagnostic-events-shared-bus

Conversation

@aiwithabidi
Copy link
Copy Markdown

@aiwithabidi aiwithabidi commented Feb 17, 2026

Summary

The diagnostic event bus in src/infra/diagnostic-events.ts uses module-level variables (let seq and const listeners = new Set()). When Rolldown (or tsdown) splits this module into separate chunks — one for the gateway and one for the plugin-SDK — each chunk gets its own copy of these variables. The gateway emits events to one listeners Set, while plugins (like diagnostics-otel) register on a different one. They never meet, so zero diagnostic events reach any plugin.

This PR replaces the module-level state with a globalThis[Symbol.for()] singleton, ensuring a single shared bus across all chunks in the same process. This is the same pattern already used in src/plugins/runtime.ts for the plugin registry (REGISTRY_STATE).

Changes

  • 1 file changed: src/infra/diagnostic-events.ts
  • Replace let seq + const listeners with a globalThis[Symbol.for("openclaw.diagnosticEventBus")] singleton
  • All public API functions (emitDiagnosticEvent, onDiagnosticEvent, resetDiagnosticEventsForTest) reference the shared bus object
  • No API surface changes — fully backwards compatible

Root Cause

Bundle chunk Variable Result
dist/pi-embedded-*.js (gateway) listeners$1 Gateway emits here
dist/plugin-sdk/reply-*.js (plugins) listeners$1 Plugins listen here

Two separate Sets — events emitted to one are invisible to the other.

Fix Pattern

// Before (broken across chunks):
let seq = 0;
const listeners = new Set<...>();

// After (shared via globalThis):
const DIAGNOSTIC_BUS = Symbol.for("openclaw.diagnosticEventBus");
const bus = (() => {
  const g = globalThis as any;
  if (!g[DIAGNOSTIC_BUS]) {
    g[DIAGNOSTIC_BUS] = { seq: 0, listeners: new Set() };
  }
  return g[DIAGNOSTIC_BUS];
})();

Test Plan

  • Existing diagnostic-events.test.ts tests pass (same public API)
  • pnpm build succeeds — Symbol.for("openclaw.diagnosticEventBus") appears in all relevant output chunks
  • Deployed on a live gateway with the diagnostics-otel plugin — confirmed the OTel plugin initializes and receives events
  • Verified diagnostics-otel: logs exporter enabled (OTLP/HTTP) in gateway logs after processing Telegram messages

Greptile Summary

Fixes diagnostic event bus isolation caused by Rolldown chunk splitting by replacing module-level seq and listeners variables with a globalThis[Symbol.for()] singleton. This ensures the gateway and plugin-SDK share a single event bus instance at runtime.

  • Replaces let seq and const listeners with a shared globalThis[Symbol.for("openclaw.diagnosticEventBus")] singleton object
  • Follows the same pattern already established in src/plugins/runtime.ts (REGISTRY_STATE)
  • No API surface changes — all public functions (emitDiagnosticEvent, onDiagnosticEvent, resetDiagnosticEventsForTest) continue to work identically
  • Typing is clean: uses typeof globalThis & intersection instead of as any, consistent with project style guidelines

Confidence Score: 5/5

  • This PR is safe to merge — it applies a well-established pattern to fix a real cross-chunk isolation bug with no API surface changes.
  • Single file changed, follows an existing proven pattern (plugins/runtime.ts), no API surface changes, existing tests cover the public API, and the fix has been validated in production. The typing is clean and avoids any.
  • No files require special attention.

Last reviewed commit: 77b8387

(2/5) Greptile learns from your feedback when you react with thumbs up/down!

Context used:

  • Context from dashboard - CLAUDE.md (source)

@sebslight
Copy link
Copy Markdown
Member

Closing as duplicate of #11168. If this is incorrect, please contact us.

@sebslight sebslight closed this Feb 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants