Skip to content

fix(observability): make Phoenix OTLP tracing work for programmatic sourcehunt runs - #243

Merged
whatever merged 4 commits into
mainfrom
fix/phoenix-bearer-auth
Sep 23, 2026
Merged

whatever merged 4 commits into
mainfrom
fix/phoenix-bearer-auth

Conversation

@jorge-garcia-le

@jorge-garcia-le jorge-garcia-le commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Phoenix/OTLP traces were not appearing for sourcehunt runs. Two independent bugs were responsible; this branch fixes both.

1. OTLP ingestion was authenticated with the wrong header

Phoenix authenticates OTLP over Authorization: Bearer <token>. Clearwing sent an api_key header, which Phoenix rejects with 401. Because export runs under BatchSpanProcessor, the failure is swallowed — no crash, no log, spans silently dropped.

  • otel.py: when PHOENIX_API_KEY is set (and no standard OTLP headers already override), send authorization: Bearer <key> instead of api_key.

2. Programmatic runs never bootstrapped tracing

OTLP tracing is wired up in clearwing:main() (CLI) and create_app() (web). Callers that drive SourceHuntRunner in-process bypass both entrypoints, so the global provider stayed the no-op ProxyTracerProvider and every span was discarded before auth even mattered.

  • runner.py: arun() (the single async entry all runs funnel through, including the sync run() wrapper and direct .arun() callers) now calls ObservabilityIntegration.bootstrap_from_env() as its first statement. It is idempotent (process-wide singleton) and a no-op unless OTLP export is configured, so it stays safe under the CLI/web paths that already bootstrap.
  • runner.py: run() calls force_flush() in a finally so a top-level invocation drains its spans before returning to the caller (e.g. before a campaign advances to the next repo), without disconnecting the shared provider.

Scope / behavior

  • CLI (clearwing sourcehunt ...) and the --machine-fd subprocess path already bootstrapped via main(); they are unaffected except for the auth fix and an eager per-run flush.
  • The bootstrap change is what makes in-process/programmatic runs (eval harness et al.) actually export.

Testing

  • tests/test_otel.py updated for the Bearer header.

Phoenix authenticates OTLP trace ingestion with an Authorization: Bearer
header. Clearwing sent PHOENIX_API_KEY as an "api_key" header, which
Phoenix rejects with 401. The BatchSpanProcessor swallows the export
failure, so traces were dropped silently with no error surfaced.

Send "Authorization: Bearer <key>" instead. Standard OTEL_EXPORTER_OTLP
header variables still take precedence when set.
…t runs

SourceHuntRunner.run()/arun() are reached directly by the eval harness,
the sourcehunt agent tool, notebooks and per-repo campaign runs, all of
which bypass the CLI and web entrypoints that call
ObservabilityIntegration.bootstrap_from_env(). Without a bootstrap the
process-wide tracer provider stays the no-op proxy, so instrumented LLM
spans are silently dropped and never reach Phoenix.

Bootstrap from env at the top of arun() -- the single async entry every
run funnels through -- and force_flush() at the run() boundary so each
run's spans are exported before the caller exits or advances to the next
repo. Both are no-ops when OTLP export is not configured and bootstrap is
idempotent, so CLI/web behavior is unchanged.
@jorge-garcia-le
jorge-garcia-le marked this pull request as ready for review September 23, 2026 18:57

@whatever whatever 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.

much appreciated!

@whatever
whatever merged commit 48a7683 into main Sep 23, 2026
1 check passed
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