Repository navigation
fix(mothership): close a pre-aborted SSE stream, keep the new-chat effort across a failed first send - #8754
Merged
Conversation
…fort across a failed first send - createSSEStream closes at once when the request aborted before start(), instead of subscribing until rotation. - The new-chat effort pick is dropped by useChat when its chatless surface is left, not by each composer's unmount, so a failed first send keeps it and the pending chat view shows it. - The chat response's effort is optional, so a new client loads chats from a server that predates it.
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
|
A first send stopped before admission adopted its chat without moving the pick, so the next new chat on the same Home mount showed and sent it. adoptResolvedChatId now drops the pick when the surface leaves the new chat. The rollback restore is gone: nothing clears the pick while a send is pending, and it overwrote a pick made during the send.
Collaborator
Author
Collaborator
Author
|
@cubic-dev-ai review this PR |
Contributor
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
This branch was previously deployed
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 join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Summary
Three findings from the #8748 release review.
createSSEStreamadds itsabortlistener insidestart(), and that listener never fires for a signal that's already aborted. A client that left while the route was still authorizing got a stream that subscribed anyway and held its handler, timers and arevalidatequery every 30 s until rotation (~16.5 min).start()now closes at once withabortedbefore taking any subscription. Covers all three callers: the desktop inbox stream, mothership events andcreateWorkspaceSSE.ModelSelectorcleared the pick on unmount whenever it had no chat id. A first send swaps the empty-state composer for the chat view, so the pick was wiped mid-send: the chat view showed the default while pending, and oncerollbackOptimisticSendrestored the pick, the swapped-back composer's unmount wiped it again. The pick belongs to the chatless surface, not one composer, souseChatnow owns its lifetime: it drops when the surface unmounts, switches chats, or adopts a chat (adoptResolvedChatId, so a first send stopped before admission doesn't carry it into the next new chat). Admission still moves it onto the new chat viaadoptNewChatEffortfirst. TherollbackOptimisticSendrestore is gone: nothing clears the pick while a send is pending, and it overwrote a pick made in the chat view during the send. Covers everyuseChatsurface: Home, organization home and the workflow copilot panel.effortis optional on the chat response. It was required, so during a deploy's traffic shift a new client hitting an old server failed contract validation and the chat didn't load.parseChatHistoryalready maps a missing value tonull;MothershipChatHistoryis unchanged.Type of Change
Testing
Each new test fails on staging:
never subscribes when the request aborted before the stream started(sse-endpoint.test.ts):expected false to be true, the body never finishes.keeps the new-chat effort across the composer swap of a first send that fails(use-chat.dom.test.tsx, renders the realModelSelectorin a Home-shaped swap):expected 'High' to be 'Low'while pending, and with that line removed,expected null to be 'low'after the failure.keeps a new-chat effort picked while the first send is pending when that send fails:expected 'Low' to be 'Medium'with the rollback restore.starts the next new chat at the default after a first send stopped before admission:expected 'Low' to be 'High'without the adoption clear.drops an unsent new-chat effort when the surface leaves the page / opens another chatpins the remaining abandonment paths on the new layer.loads a chat from a server that predates the effort field(mothership-chats.test.ts):Response failed contract validation — chat.effort.apps/simtypecheck, biome,check:api-validation,check:api-contract-routes,check:openapi,check:comment-hygiene,check:test-patternsand the home, hooks/queries, contracts and sse-endpoint suites (1031 tests) pass.Checklist