Skip to content

fix(tracing): seed log context before route-thinking to fix gen_ai.conversation.id on gen_ai.chat spans#613

Closed
dcramer wants to merge 1 commit into
mainfrom
fix/route-thinking-conversation-id
Closed

fix(tracing): seed log context before route-thinking to fix gen_ai.conversation.id on gen_ai.chat spans#613
dcramer wants to merge 1 commit into
mainfrom
fix/route-thinking-conversation-id

Conversation

@dcramer

@dcramer dcramer commented Jun 17, 2026

Copy link
Copy Markdown
Member

Problem

In the queue-worker path (POST /api/internal/agent/continue), gen_ai.conversation.id is missing from the gen_ai.chat span emitted during thinking-level routing (the Haiku router call).

OTel GenAI semconv marks gen_ai.conversation.id as Conditionally Required on gen_ai.chat and gen_ai.invoke_agent spans. All other gen_ai.* spans in Junior already carry it — this was the one gap.

Root cause

gen_ai.conversation.id propagates via AsyncLocalStorage. spanContext (which has conversationId) is computed before selectTurnThinkingLevel is called, but hasn't been installed into ALS yet at that point. So the chat.route_thinking + its nested gen_ai.chat span both miss the attribute in queue-worker turns.

The Slack inline path is unaffected: slack-runtime.ts wraps the full turn in withSpan('chat.turn', ..., context) which seeds ALS with conversationId before selectTurnThinkingLevel runs.

Fix

Wrap the awaited selectTurnThinkingLevel call in withLogContext(spanContext, ...). This is a scoped seed — it only covers the route-thinking subtree, leaves everything else unchanged, and works identically in both code paths.

- thinkingSelection = await selectTurnThinkingLevel({ ... });
+ thinkingSelection = await withLogContext(spanContext, () =>
+   selectTurnThinkingLevel({ ... }),
+ );

Verification

After this change, in queue-worker turns:

  • chat.route_thinking has gen_ai.conversation.id
  • gen_ai.chat inside route-thinking has gen_ai.conversation.id
  • All existing spans unaffected ✓

View Session in Sentry

…nversation.id on gen_ai.chat spans

In the queue-worker path (POST /api/internal/agent/continue), spanContext
is computed before selectTurnThinkingLevel runs but is not yet installed
into AsyncLocalStorage. As a result, the gen_ai.chat span emitted by the
Haiku router call does not inherit gen_ai.conversation.id, violating OTel
GenAI semconv which marks that attribute Conditionally Required on all
gen_ai.chat spans.

The Slack inline path is unaffected because slack-runtime.ts wraps the
full turn in withSpan('chat.turn', ..., context) which seeds ALS with
conversationId before selectTurnThinkingLevel runs.

Fix: wrap the awaited selectTurnThinkingLevel call in withLogContext(spanContext)
so the route-thinking subtree (chat.route_thinking + its gen_ai.chat span)
inherits gen_ai.conversation.id in both execution paths.

Co-Authored-By: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
@vercel

vercel Bot commented Jun 17, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
junior-docs Ready Ready Preview, Comment Jun 17, 2026 9:31pm

Request Review

@dcramer

dcramer commented Jun 17, 2026

Copy link
Copy Markdown
Member Author

Closing — the premise is wrong per OTel semconv. The router (Haiku) call is a one-shot classifier with no conversation context, so its gen_ai.chat span correctly has no gen_ai.conversation.id. Spec footnote [4] says to only set it when an identifier is available for that operation. Trace parent-child links are the right mechanism here. The actual investigation this spun out of (missing auth-prompt trace) is a separate, still-open question.

@dcramer dcramer closed this Jun 17, 2026
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