Repository navigation
cmux NEXT: an upload holds its Send's gesture ticket, so a file of any size still sends - #25810
Conversation
…y size still sends
The person's Send reserves one ticket for its prompt and chat
(transport.gesture {session/prompt, promptId, sessionId}). Each uploads.begin
carries {sessionId, promptId, ticket}; the host checks the ticket is this
connection's, for exactly that chat and prompt, and fresh, then holds it: the
ticket's clock stands still while any of its uploads makes progress (a stall
past 60 s ends the hold). Finish and cancel release the hold; a newer Send, a
prompt that went out, the page's release and the connection's end drop the
ticket and report the uploads that held it. The prompt redeems it once, only
to that chat and only after every upload released it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-12c9da9c364ba148511a
|
Owned by an agent session · session |
|
Bead |
|
Found 6 test failures on Blacksmith runners: Failures
|
Review (Codex): progress of one upload, or a new upload, refreshed a shared progress time, so a stalled upload could keep the ticket alive. Each upload now keeps its own last progress, and any one stalled ends the hold. The redeem doc says why an unbound prompt ticket (trust or harness-switch hold) stays as it was: it never holds an upload. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-12c9da9c364ba148511a
|
cmux status (one comment, edited in place; each line is updated by the job that owns it) Review App of this head (app.zip, kept 1 day)
red: cmux-next checks (god files, concurrency, crash safety, l10n); no finished base run on c5dd46c to compare; do not merge until green; see lane-rules |
|
Review (Codex gpt-6-astra xhigh, read-only, independent): SHIP at 9b6b4c9 First pass at 9dc75be BLOCKED on two findings: a shared stall clock (fixed in 9b6b4c9: each upload has its own) and unbound prompt tickets (the existing trust and harness-switch hold, unchanged; an unbound ticket can never hold an upload). The re-review of the delta found nothing. Agent session: agent session |
…holds With a stall clock per upload, the test's second upload, held but silent for twenty minutes, rightly ended the hold. Both now report progress while they run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-12c9da9c364ba148511a
|
Review (Codex gpt-6-astra xhigh, read-only, independent): SHIP at 03a324d The delta from 9b6b4c9 is test-only: the long-upload test now reports progress for both uploads it holds, which the per-upload stall clock requires. Agent session: agent session |
|
Shipped in:
|
…ture
chat.upload.begin may carry the Send's {sessionId, promptId, ticket}.
Such a begin holds that ticket on the transport (holdSendTicket, the
holdForUpload contract #25810 built for uploads.begin) instead of
spending the person's one gesture credit, so the prompt it was uploaded
for still goes: it redeems the ticket once every upload holding it has
finished or been cancelled. A ticket of another prompt or chat, or a
forged one, is refused before the stager. Chunks report progress (a
stalled or replaced hold cancels its stage); finish and cancel release
the hold. A begin without the three fields is unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-0fec927bf7d7f6f85883
![Fix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/fix-with-codesmith-light-3x.png)
Leo, 2026-10-26: "why the FUCK SHOULD THERE BE A FUCKING SIZE CAP ANYWAYS JUST FUCKING / I ALREADY SAID WE NEED ARBITARRILY LARGE FILES OF ANY TYPE"
This is the send-ticket contract the composer lane's no-cap upload PRs (#25730 and the native
uploads.beginfoundation) build on. It answers the review finding that held them: a real Send's gesture is gone after 30 s, an arbitrarily large upload takes longer,uploads.begincarried no promptId and no ticket, and a longer generic gesture TTL is not acceptable.Change
A small transaction capability on the host's gesture store (
AgentPaneUserGestures). No gesture or ticket lifetime changes.transport.gesture {intent: {method: "session/prompt", params: {promptId, sessionId}}}.sessionIdbinds the ticket and is not part of the pick (AgentPaneGestureIntent.bindingKeys). A chat that is not this pane's gets no ticket.uploads.begincarries{sessionId, promptId, ticket}(AgentPaneUploadSendTicket(beginParams:); any of them missing or empty refuses the upload).AgentPaneTransport.holdSendTicket(_:upload:sessionId:promptId:): the transport is connected and the chat is the pane's.AgentPaneUserGestures.holdForUploadthen checks that the ticket is this connection's, bound to exactly that chat and that promptId, and still fresh. While any of its uploads is in progress, the ticket's clock stands still. The suspension holds only while bytes move: an upload that reports no progress (uploadProgressed) foruploadStallLimit(60 s) ends the hold and the ticket. The check is lazy, with no timer.releaseUpload. When the last upload is released, the clock runs again from where it stopped. A newer Send's ticket (same slot, same connection), a prompt that goes out (the existingclearTicketsafter a sent prompt),transport.gesture.releaseand the connection's end each drop the ticket. That coversclose(connection:), a daemon-side close (new:clearTickets(connection:)) and a reconnect. Dropping a ticket reports the uploads that held it throughonUploadHoldsEnded, so the uploader cancels them.redeem(..., sessionId:)), redeems the ticket once, and only after every upload released it. A prompt sent while an upload still holds the ticket spends it and is refused.Not in this PR: the
uploads.beginpage op, the uploader and the webview's reserve at Send. Those are the composer lane's foundation PR, which callsholdSendTicket,uploadProgressedper acknowledged frame,releaseUploadat finish and cancel, and setsonUploadHoldsEnded. #25774's section 1b should addpromptIdandticketto theuploads.beginparams; that is the docs PR owner's change.Risk: high tier (relay gesture policy). Contained: a ticket with no
sessionIdbinding behaves exactly as before, except that it now also ends with its connection (it could not be redeemed on another connection anyway).Tests
AgentPaneUploadSendTicketTests(CmuxNextAgentPaneTests):anUploadLongerThanTheTicketStillSends: two uploads held for 1200 s and 3600 s, past the 30 s gesture and the 600 s ticket lifetime. The prompt then redeems the ticket. Control: the same wait without a hold expires it.aPromptWhoseUploadTookAnHourGoesOutAndOnlyOnce: end to end throughAgentPaneTransporton the stand-in acpmux. With no live gesture, the prompt goes out after an hour-long held upload, and a second prompt with the same ticket istransport.gesture_required.anExpiredReusedOrForeignTicketHoldsNoUpload: another connection, chat or prompt, a forged ticket, one upload held twice, a ticket with no chat binding, a redeemed ticket and an expired ticket are all refused.onlyItsOwnPromptInItsOwnChatRedeemsItOnceItsUploadsEnded,aStalledUploadEndsItsHold,oneStalledUploadEndsTheHoldEvenBesideAnActiveOne(each upload has its own stall clock).finishOrCancelReleasesTheHoldAndTheClockRunsAgain,replacementReleaseAndConnectionEndDropTheHolds(newer Send, release or prompt sent, connection end), andtheConnectionsEndDropsItsHolds(throughtransport.close).theIntentBindsASendTicketToItsChatOnly,theBeginParamsCarryTheTicketPromptAndChat.Observed / Not run
cmux-ci run --class light --script scripts/ci/package-test-lane.sh ... --label suites, aws-m4pro-4). At head 03a324d (step 6fb2da11d4a80b6fe991b533):AgentPaneUploadSendTicketTests(11 tests) plus 10 neighbouring gesture and relay suites (RelayedAllow, SlowStartPrompt, RemoteAcpmuxWireAnswer, SendPickedFolderPrompt, AskAnswer, UnsentPrompt, FullAccessRelay, HarnessEnableRelay, SecretRelay, PostedClick), all 11 suites passed.anUploadLongerThanTheTicketStillSendsfailed. The test held two uploads but reported progress for only one, and the new per-upload stall clock ended the hold, as designed. Fixed in the test (03a324d).No capture: host-side gesture policy with no UI; nothing on screen changes.
Changelog
none
Agent session: agent session
🤖 Generated with Claude Code