fix: inbound replay span not sent to CLI in Django/FastAPI REPLAY mode#71
Conversation
|
bugbot run |
✅ Generated 18 tests - 18 passed (7c99402) View tests ↗Test Summary
ResultsTusk's tests all pass and directly validate the two critical bug fixes in this PR. The Django tests confirm that View check history
Was Tusk helpful? Give feedback by reacting with 👍 or 👎 |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.
Summary
replay_trace_id_contextwas reset beforespan.end(), causing the SDK to silently drop inbound replay spansOUTPUT_VALUEon the server span in Django's REPLAY mode so the inbound span includes actual response dataProblem
A production customer's validation run showed test results marked as "passed" with visible deviations in the UI — the "Expected vs Actual" diff panel showed the full expected response on the left and an empty
{}onthe right.
Why the CLI said "passed": The CLI determines pass/fail by comparing the raw HTTP response it receives directly from the replayed request against the expected response. This comparison was correct — the responses matched.
Why the UI showed a deviation: The UI renders the diff using
span_recording.outputValueRedacted(expected) vsspan_result_recording.outputValueRedacted(actual). The actual side was empty because the SDK never sent the inbound replay span to the CLI, so nospan_result_recordingwas created.Root Causes
Bug 1: Context ordering (Django + FastAPI)
In the
finallyblock of both_handle_replay_requesthandlers,replay_trace_id_context.reset()was called beforespan.end(). SinceTdSpanProcessor.on_end()fires synchronously insidespan.end()and readsreplay_trace_id_context.get()to get the trace ID, the context was already gone — causing the processor to log an error and silently drop the span. The CLI never received the inbound replay span.Bug 2: Missing OUTPUT_VALUE (Django only)
Django's _handle_replay_request did not capture response data on the span. The comment said "don't capture the span (it's already recorded)" — but the response OUTPUT_VALUE is needed on the replay span so the CLI can forward it to the backend for UI display. Added _capture_replay_output() which sets OUTPUT_VALUE on the span without the RECORD-mode concerns (transforms, trace blocking, schema merges).
Why E2E tests didn't catch this
The e2e tests only validate the passed boolean from the CLI's JSON output. Since the CLI's pass/fail is based on its own HTTP response comparison (not the inbound span data), all tests passed. The missing span data only affects the UI's "Expected vs Actual" display, which e2e tests don't verify.
Note
Medium Risk
Touches request middleware/instrumentation teardown ordering and span attribute capture, which can affect tracing/export behavior across REPLAY flows, but changes are localized and straightforward.
Overview
Fixes REPLAY-mode span exporting in Django and FastAPI by ending the server span before resetting
replay_trace_id_context, ensuringTdSpanProcessor.on_end()can still route the inbound replay span to the CLI.In Django REPLAY mode, the middleware now also captures and sets
TdSpanAttributes.OUTPUT_VALUEon the server span (with HTML normalization) so the backend/UI can show the actual response in “Expected vs Actual” diffs.Adds internal guidance to
.cursor/BUGBOT.mddocumenting the required REPLAY ordering andOUTPUT_VALUEbehavior; makes a small cleanup to the Django e2e.tuskconfig.Written by Cursor Bugbot for commit 7c99402. This will update automatically on new commits. Configure here.