Fix swarm traces showing all spans starting from 0.0s (BB-153) - #4461
Fix swarm traces showing all spans starting from 0.0s (BB-153)#4461ZeHuari wants to merge 1 commit into
Conversation
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>
|
Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits. |
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
Internal previewPreview URL: https://mcp-inspector-pr-4461.up.railway.app |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review. WalkthroughThe change adds a shared Merge Risk: ⚪ Minimal · up to 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. Comment |
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:
runDirectChatTurnbuilds a freshcreateAiSdkEvalTraceContext(turnStartedAt)for each one, andcreateOffsetIntervalwrites offsets relative to that anchor. So every turn's blob independently starts atstartMs: 0.That's fine in isolation. The problem is how a finished session is reassembled: both readers fetched every turn's blob and did
with no re-anchoring — so all N turns were laid on top of each other at zero, and
leftPercent = (startMs / axisMaxMs) * 100intrace-timeline.tsxfaithfully drew what it was given.There's already a
traceStartedAtescape hatch onrunDirectChatTurnwhose 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:
rebaseTraceSpansalready exists inshared/live-chat-trace.ts— the live chat path uses it, the persisted path just never did.SharedChatTurnTrace.startedAtalready 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
hydrateSpanswas duplicated byte-for-byte in two files, both with the bug:components/swarms/use-persisted-session-trace.ts— the Swarm trace from the reportcomponents/connection/share-usage/ShareUsageThreadDetail.tsx— Share usage, same symptomBoth now call one extracted
hydrateTurnTraceSpans. It takes a structural{ startedAt, spansBlobUrl }rather than the fullSharedChatTurnTrace, which keeps it testable without building a whole row.The base is the earliest
startedAtof the turns passed in, which is the same valueShareUsageThreadDetailalready computes for itstraceStartedAtMsanchor — 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 nospansBlobUrlstill counts toward the session start, input order doesn't matter, non-timing fields survive the rebase, and aNaNstartedAtcan't poison the base for everyone else.Verification
typecheck:client: clean across all client code.@mcpjam/sdkwon't build on my machine (ERR_WORKER_OUT_OF_MEMORYin 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 cleanmainwith this branch stashed. Leaving that to CI.Known gap, not addressed here
The Swarm
TraceViewer(journey-run-results.tsx) doesn't receivetraceStartedAtMsat all, so its hover tooltip has no absolute wall-clock time —ShareUsageThreadDetaildoes 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.
hydrateTurnTraceSpansused by bothusePersistedSessionTraceandShareUsageThreadDetail.startedAtvalues.Written for commit b072630. Summary will update on new commits.