Repository navigation
fix(mothership): decide once whether a queued send may already be on the server - #8727
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
|
@cubic-dev-ai review this PR |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
|
|
@cubic-dev-ai review this PR |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
All reported issues were addressed across 8 files
Reply with feedback, questions, or to request a fix.
Fix all with cubic | Turn on auto-fix | Re-trigger cubic
|
@cubic-dev-ai review this PR |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
|
@cubic-dev-ai review this PR |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
All reported issues were addressed across 10 files
Reply with feedback, questions, or to request a fix.
Fix all with cubic | Turn on auto-fix | Re-trigger cubic
|
@cubic-dev-ai review this PR |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
…ainst edits A Send-now whose Stop settled has its POST out under the id its stored handoff carries. Restored after a reload it had no resumeUserMessageId, so the queue guard missed it, and an edit could run as a second turn. The guard now treats a settled handoff's id as an earlier attempt. A direct send can't reach the never-sent branch (a pending Stop queues it), so that restore now treats anything but a refusal as unknown. Docs: the 1-hour claim TTL, the claim failing closed, and the superseded admission conflict classed as busy.
Admission's superseded conflict (another attempt with this id re-took the claim) went out as a bare 409, which the client reads as a busy refusal and marks the message editable. The claim's 60s in-progress TTL can run out before admission, since branch, attachment and context preparation precede it, so that attempt may admit the turn. The route now answers it as a duplicate naming the send's id, and the client keeps the message under it.
…e server Whether a queued message may already be a turn on the server was inferred in several places (the store guard, a stopRequired exemption, the handoff restore, notAdmitted/neverSent), and the stopRequired exemption was wrong: a Send-now reuses a re-queued message's earlier id, which may have been sent. startSendMessage now decides it where it chooses the id (a reused id carries its entry's flag, a fresh one is unsent until its POST, a refusal clears it) and carries that one fact on the withdrawal result, the stored handoff and the queue entry. The store guard reads only the flag, filling it in only for writers with no say.
…after rebase - one helper names the id a queued entry reuses (its Stop handoff's, else the withdrawn send's), in the order startSendMessage sends it - a conflict naming the send's own id is never read as a refusal - the stored handoff records the flag as it stands, rewritten as the POST goes out, instead of inferring it from stopRequired - the Stop-settlement test's handoff carries the flag the hook writes; a flagless legacy handoff is checked against history before it is resent
c677e91 to
97f5c94
Compare
|
@cubic-dev-ai review this PR |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
…flict - a fresh Send-now reloaded with its POST unanswered restores uneditable (red without the handoff rewrite as the POST goes out) - a conflict naming the resent id itself is a duplicate, never a refusal (red without the conflictStreamId !== userMessageId check)
|
@cubic-dev-ai review this PR |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
Summary
Follow-up to #8723. This closes the remaining ways a queued message the server may already hold could be edited into a second turn, and decides that question in one place instead of several.
stopRequiredexemption, the stored-handoff restore, and thenotAdmitted/neverSentresults. The exemption was wrong: a Send-now reuses a re-queued message's earlier id, which may already have reached the server.startSendMessagenow decides it where it chooses the id. A reused id carries its queue entry'sadmissionUnknown, and is unknown when the entry doesn't say. A fresh id is unsent until its POST goes out. Only the server refusing the id clears it.notAdmitted/neverSentpair and thestopRequiredexemption are gone.ChatSendSupersededError, and the route answers it as a 409 naming the send's own id, so the client keeps the message under that id.claimChatSendfails closed with a 500 (its storage is forced to Postgres), not open. The admission flag's comment notes the 1-hour claim TTL.Type of Change
Testing
keeps a resumed Send-now uneditable when the page reloads before its Stop settles: the reviewer's probe, end to end. It fails on this PR's previous head.guards a Send-now restored from its stored handoff, for both values of the stored flag. Fails on staging.keeps a send answered as a duplicate with no stream uneditable, then retries it.reads a reused handoff id as possibly sent unless the entry says otherwise. Fails on staging.answers a send superseded at admission as a duplicate naming its id. Fails against the staging route.refuses to admit a send whose claim another attempt took. Fails on staging.Checklist