Skip to content

fix: prevent duplicate LLM spans when multiple SDK instances coexist (BT-5139)#1973

Open
Stephen Belanger (Qard) wants to merge 3 commits intomainfrom
fix-BT-5139
Open

fix: prevent duplicate LLM spans when multiple SDK instances coexist (BT-5139)#1973
Stephen Belanger (Qard) wants to merge 3 commits intomainfrom
fix-BT-5139

Conversation

@Qard
Copy link
Copy Markdown
Contributor

Summary

  • When the Braintrust SDK is loaded from two different module paths in the same process (e.g. a cloud runner's bundled copy + the user's scorer dependency), each PluginRegistry instance independently subscribed to the same process-global diagnostics_channel. Every OpenAI API call fired one channel event, but with two subscribers, two LLM spans were created — the duplicate spans reported in BT-5139.
  • Fix adds a Symbol.for("braintrust.registry.enabled") guard on globalThis so only the first PluginRegistry to call enable() registers channel subscriptions. disable() clears the flag so test enable/disable cycles work correctly.
  • Regression test added: creates two registry instances, enables both, asserts only the first one's isEnabled() returns true.

Test plan

  • pnpm exec vitest run src/instrumentation/registry.test.ts — all 15 tests pass
  • Verified new test fails on old code (instanceB.isEnabled() returns true without the fix) and passes with the fix

🤖 Generated with Claude Code

Stephen Belanger (Qard) and others added 3 commits May 8, 2026 16:45
…ces coexist

When the Braintrust SDK is loaded from two different module paths in the
same process (e.g. a cloud runner's bundled copy + the user's scorer
dependency), each PluginRegistry instance independently subscribed to the
same process-global diagnostics_channel, causing every OpenAI call to
produce two LLM spans (BT-5139).

Fix uses Symbol.for("braintrust.registry.enabled") on globalThis as a
cross-instance guard: only the first PluginRegistry to call enable() sets
up channel subscriptions; subsequent instances skip. disable() clears the
flag so test enable/disable cycles continue to work.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ndalone global flag

The previous approach stored a boolean on globalThis[Symbol.for("braintrust.registry.enabled")],
which was not cleared when vi.resetModules() discards the module cache in tests —
causing the edge-runtime-bootstrap tests to fail on the second test in each
describe.each block.

The braintrust state (globalThis[Symbol.for("braintrust-state")]) is already
shared across all SDK instances loaded in the same process via
_internalSetInitialState, and is deleted by the test cleanup in
restoreRuntimeEnvironment. Storing the registry marker on that object naturally
handles both scenarios:

- BT-5139: two concurrent SDK instances share the same state → second instance
  blocked by the marker left by the first.
- vi.resetModules(): test deletes the state before re-import → fresh state has
  no marker → new module instance can subscribe.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant