Skip to content

Fix swarm traces showing all spans starting from 0.0s (BB-153) - #4461

Open
ZeHuari wants to merge 1 commit into
mainfrom
fix/bb-153-swarm-trace-span-offsets
Open

Fix swarm traces showing all spans starting from 0.0s (BB-153)#4461
ZeHuari wants to merge 1 commit into
mainfrom
fix/bb-153-swarm-trace-span-offsets

Conversation

@ZeHuari

@ZeHuari ZeHuari commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator

Fixes BB-153 — reported by Prathmesh in #C0BPKNN2BND.

What was broken

Opening a completed Swarm session and switching to the Trace tab drew every span flush against the 0ms mark. The bars had the right lengths (33.0s, 11.6s, 14.6s…) but all started at the same place, so the sequencing and the waits between turns were invisible — which is most of what the trace view is for.

Why

Spans are recorded per turn: runDirectChatTurn builds a fresh createAiSdkEvalTraceContext(turnStartedAt) for each one, and createOffsetInterval writes offsets relative to that anchor. So every turn's blob independently starts at startMs: 0.

That's fine in isolation. The problem is how a finished session is reassembled: both readers fetched every turn's blob and did

return results.flat();

with no re-anchoring — so all N turns were laid on top of each other at zero, and leftPercent = (startMs / axisMaxMs) * 100 in trace-timeline.tsx faithfully drew what it was given.

There's already a traceStartedAt escape hatch on runDirectChatTurn whose docstring calls out exactly this failure ("otherwise each turn's spans collapse to start-at-zero"), but the only caller passing it is the eval runner — chat and swarm don't.

The renderer was never at fault. The data was.

The fix

Each turn is shifted by its own distance from the session start:

rebaseTraceSpans(spans, trace.startedAt - sessionStartedAt)

rebaseTraceSpans already exists in shared/live-chat-trace.ts — the live chat path uses it, the persisted path just never did. SharedChatTurnTrace.startedAt already carries the absolute epoch, so no new data had to be captured or plumbed.

Wall-clock offsets, deliberately. The idle time between turns stays visible as empty space rather than being packed away. That is what the ticket asks for ("correct relative offsets based on their actual start times") and it is the honest timeline for debugging latency. Worth knowing: the live view packs turns contiguously by duration (nextOffsetMs += durationMs), so the same session can look slightly different mid-run vs. after it finishes. Unifying the two is a bigger change and isn't in scope here.

Fixed on the read side, not the write site. Anchoring spans at the session start when they're recorded would only help sessions created after the deploy; doing it on read means every session already in the database renders correctly, with no backfill. The two are mutually exclusive — doing both would double-shift — so if we ever move this to the producer, this call has to come out.

Scope

hydrateSpans was duplicated byte-for-byte in two files, both with the bug:

  • components/swarms/use-persisted-session-trace.ts — the Swarm trace from the report
  • components/connection/share-usage/ShareUsageThreadDetail.tsx — Share usage, same symptom

Both now call one extracted hydrateTurnTraceSpans. It takes a structural { startedAt, spansBlobUrl } rather than the full SharedChatTurnTrace, which keeps it testable without building a whole row.

The base is the earliest startedAt of the turns passed in, which is the same value ShareUsageThreadDetail already computes for its traceStartedAtMs anchor — so offsets and the absolute-clock tooltip agree by construction.

Only the persisted path is affected. The live Swarm SSE stream doesn't carry spans at all, which is why this only ever showed up on sessions reopened after the fact — consistent with the "about 3 hours ago" session in the report.

Tests

New client/src/components/evals/__tests__/turn-trace-spans.test.ts (9 tests) — there was no coverage on this path before. Beyond the happy path it pins the edges that made the old code look plausible: a turn whose blob 404s must not drag the others, a turn with no spansBlobUrl still counts toward the session start, input order doesn't matter, non-timing fields survive the rebase, and a NaN startedAt can't poison the base for everyone else.

Verification

  • New tests: 9/9 pass.
  • typecheck:client: clean across all client code.
  • Not verified locally: the full inspector suite. @mcpjam/sdk won't build on my machine (ERR_WORKER_OUT_OF_MEMORY in the DTS step, even at 8GB), which leaves stale SDK types that break ~24 unrelated test files on module resolution. Confirmed pre-existing — the same files fail identically on a clean main with this branch stashed. Leaving that to CI.

Known gap, not addressed here

The Swarm TraceViewer (journey-run-results.tsx) doesn't receive traceStartedAtMs at all, so its hover tooltip has no absolute wall-clock time — ShareUsageThreadDetail does pass one. Unrelated to the span offsets and worth its own change.

🤖 Generated with Claude Code


Summary by cubic

Fixes persisted Swarm and Share usage traces where every span started at 0.0s by re-anchoring each turn's spans to the session's earliest start time.

This corrects the timeline so idle time between turns stays visible, and it fixes all already-saved sessions without a backfill. The live view still packs turns contiguously, so it may look slightly different mid-run vs. after completion.

  • Extracts a shared hydrateTurnTraceSpans used by both usePersistedSessionTrace and ShareUsageThreadDetail.
  • Adds tests that cover rebasing, failed blob loads, missing URLs, and invalid startedAt values.

Written for commit b072630. Summary will update on new commits.

Review in cubic

Every turn is traced with its own `createAiSdkEvalTraceContext(turnStartedAt)`,
so the offsets inside a turn's span blob are relative to THAT turn — each blob's
first span sits at `startMs: 0`. Both persisted-session readers flattened the
blobs with `results.flat()` and no re-anchoring, stacking every turn on top of
the others, so the Trace timeline drew all spans starting at 0.0s.

Rebase each turn by its own distance from the session start
(`startedAt - min(startedAt)`), using the existing `rebaseTraceSpans` helper the
live chat path already applies. Wall-clock offsets, so the idle gaps between
turns stay visible rather than being packed away.

The two readers had a byte-identical copy of `hydrateSpans`, so this extracts
one `hydrateTurnTraceSpans` they now share.

Fixed on the read side, not at the write site, so sessions already persisted
render correctly without a backfill.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. bug Something isn't working labels Aug 28, 2026
@chelojimenez

Copy link
Copy Markdown
Contributor

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@github-actions

Copy link
Copy Markdown
Contributor

Internal preview

Preview URL: https://mcp-inspector-pr-4461.up.railway.app
Deployed commit: 58efe62
PR head commit: b072630
Backend target: staging fallback.
Health: ✅ Convex reachable
Access is employee-only in non-production environments.

@coderabbitai

coderabbitai Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1d370ba0-8989-4531-8d48-914402b4e7ca

📥 Commits

Reviewing files that changed from the base of the PR and between 26f2fa6 and b072630.

📒 Files selected for processing (4)
  • mcpjam-inspector/client/src/components/connection/share-usage/ShareUsageThreadDetail.tsx
  • mcpjam-inspector/client/src/components/evals/__tests__/turn-trace-spans.test.ts
  • mcpjam-inspector/client/src/components/evals/turn-trace-spans.ts
  • mcpjam-inspector/client/src/components/swarms/use-persisted-session-trace.ts

Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review.


Walkthrough

The change adds a shared hydrateTurnTraceSpans utility. It fetches persisted turn span blobs, preserves wall-clock gaps, handles invalid or unavailable data, and flattens spans into one session timeline. Two existing consumers now use the shared utility. New tests cover timing, field preservation, malformed data, failed loads, invalid timestamps, and empty input.

Merge Risk: ⚪ Minimal · up to b0726

This change corrects persisted trace positioning without altering stored data, access, or service behavior. It is merge-ready after normal checks, with no actionable merge-blocking risk remaining.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants