Repository navigation
Request content: keep the live answer until the stored one has landed - #334
Merged
Merged
Conversation
When a streamed request ended, the Content tab could switch from the live answer to the stored detail before the answer was on disk and show the response as not saved; reopening the request showed it in full. Core records the request row and the bodies on separate threads. The row (and `request_priced`) lands first; the request body was stored when the request started, while the heads and the answer are written after the request ends and has been redacted. `landed()` accepted the detail as soon as any one of them was there, so the request body alone was enough. - `caughtUp(d, live)` replaces `landed(d)`: the stored detail counts only once it has every head and body the live content showed (heads, client request, the serving hop's request, the client answer, and the upstream answer when it differs from what the client got) - The live subscription now runs until it closes on its own instead of stopping when the request leaves in-flight: `request_priced` can arrive before the last chunk and `end`, and stopping then lost the tail of the answer and never fetched the stored copy Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
In the request drawer's Content tab, a streamed answer types out live as expected, but right after the request finishes the answer turns into "not saved" and goes blank. Reopening the request shows the full answer.
Cause
Core records the request row and the bodies on separate threads (
tw_store::task). The row andrequest_pricedland first. The request body was stored when the request started, while the heads and the answer are written only after the request ends and has been redacted (Ending::settle→ bodies channel →for_disk→ bodies thread).landed(d)accepted the stored detail as soon as any of heads / request body / response body was present, so the request body alone was enough. The drawer switched to a detail with no response yet and nothing fetched it again.A second path made it stick: the live subscription was dropped as soon as the request left in-flight.
request_pricedcan arrive before the last chunk andend, so the subscription stopped beforeend, and the retry loop that fetches the stored copy (settle) never ran.Fix
caughtUp(d, live)replaceslanded(d): the stored detail counts only once it has every head and body the live content showed: heads, client request, the serving hop's request when it was sent separately, the client answer, and the upstream answer when it differs from what the client got (until the client copy lands,response_bodyis the upstream one). Without live content the old rule applies.useLiveContentkeeps the subscription until it closes on its own (end, error, or not running) instead of stopping when the request leaves in-flight; it still unsubscribes when the drawer closes or switches request.onClosedgets the live content sosettlecan keep polling until the stored copy has caught up.Verification
pnpm typecheck,pnpm vitest run(1206 tests pass), with 5 new cases forcaughtUpindialect.test.ts.request_pricedfirst, last chunk +end250 ms later, heads + answer on disk 2 s later). Before the fix the answer section went to "not saved" at the end of the stream; after it the answer stays and the drawer switches to the stored detail once it has landed. The default order (endbeforerequest_priced) is unchanged.🤖 Generated with Claude Code