feat(mobile): queue a message while its attachment is still uploading - #10404
Conversation
A connected composer disabled Send until every attachment finished uploading, so on a slow link the user sat watching a progress ring even though the offline path already knew how to queue the message and let the outbox drain finish the upload. Only a failed upload now blocks sending, since the drain would hit the same failure. With an upload in flight, the new-task screen queues the task instead of starting it, and both composers label the button Queue so the delayed send is not a surprise. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: Scenario and decoded snapshot size10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
ApprovabilityVerdict: Not approved Macroscope's review found this PR not approvable — This PR changes production submission behavior so messages and tasks can be queued while attachments upload, then dispatched later by the outbox. The implementation is focused and covered by targeted tests, but the new user-facing queueing workflow and altered dispatch timing merit human confirmation. Not approved because:
Review your spending limits in Billing settings. You can add or adjust custom eligibility rules. Learn more. |
…pingdotgg#10404) Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
## What's Changed * fix(clients): show feedback results in composer banners by @juliusmarminge in pingdotgg/t3code#10398 * fix(server): stop Windows terminal polling from spiking CPU by @UtkarshUsername in pingdotgg/t3code#9476 * fix(web): onboarding installs agents without needing Node or npm by @t3dotgg in pingdotgg/t3code#10402 * fix(server): allow settling threads with unanswered async questions by @t3dotgg in pingdotgg/t3code#10400 * feat(ci): ship stable releases from the latest nightly commit by @t3dotgg in pingdotgg/t3code#10410 * feat(marketing): add a nightly channel to the download page by @t3dotgg in pingdotgg/t3code#10408 * fix(web): keep settings inputs focused during IME composition by @Lucenx9 in pingdotgg/t3code#10262 * fix(server): preserve Codex reset credits during usage updates by @yashranaway in pingdotgg/t3code#10308 * docs: link the repository security reporting policy by @yashranaway in pingdotgg/t3code#10303 * fix(web): only show auto balance errors after failed checks by @maria-rcks in pingdotgg/t3code#10407 * fix(web): improve preview recording frame delivery by @maria-rcks in pingdotgg/t3code#10403 * fix(server): preserve inline provider secrets on redacted saves by @maxwellyoung in pingdotgg/t3code#10054 * fix(web, mobile): replace Apple desktop machine labels by @extoci in pingdotgg/t3code#10396 * fix(web): hide browser when the right panel starts closing by @Neel2107 in pingdotgg/t3code#10385 * fix(web): keep settings section headings description-free by @maria-rcks in pingdotgg/t3code#10415 * fix(usage): read and redeem hub reset credits through CLIProxyAPI by @juliusmarminge in pingdotgg/t3code#10395 * fix(web): deduplicate expanded tool labels and keep errors expandable by @Yash-Singh1 in pingdotgg/t3code#10420 * fix(server): skip git status scans while the index is locked by @Gigioxx in pingdotgg/t3code#9845 * fix(mcp): allow text-only preview snapshots by @juliusmarminge in pingdotgg/t3code#10232 * fix(claude): name the expired login or usage limit instead of a generic API error by @vitalyiegorov in pingdotgg/t3code#10321 * feat(mobile): queue a message while its attachment is still uploading by @juliusmarminge in pingdotgg/t3code#10404 * feat(mobile): show when an existing thread has a message waiting in the outbox by @juliusmarminge in pingdotgg/t3code#10405 * fix(codex): accept misalignment policy errors on thread resume by @realbakari in pingdotgg/t3code#10373 * fix(server): skip disabled settlement lookups by @t3dotgg in pingdotgg/t3code#10424 * fix(server): run OpenCode CLI commands sequentially by @t3dotgg in pingdotgg/t3code#10427 * feat(web): name the drop action while dragging sidebar threads by @SunkenInTime in pingdotgg/t3code#10378 * perf(web): keep the sidebar responsive during bulk thread updates by @t3dotgg in pingdotgg/t3code#10413 * fix(web): onboarding wizard now supports light mode by @t3dotgg in pingdotgg/t3code#10432 * feat(threads): dismiss async questions without replying by @t3dotgg in pingdotgg/t3code#10431 * fix(web): stop collapsing the composer when it loses focus by @t3dotgg in pingdotgg/t3code#10437 * fix(server): keep interrupted threads resumable after restarts by @maria-rcks in pingdotgg/t3code#10421 ## New Contributors * @Neel2107 made their first contribution in pingdotgg/t3code#10385 * @realbakari made their first contribution in pingdotgg/t3code#10373 **Full Changelog**: pingdotgg/t3code@v0.0.39-nightly.20260906.1316...v0.0.39-nightly.20260907.1325 Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.39-nightly.20260907.1325
Problem
A connected composer disabled Send until every attachment had finished uploading. On a slow link that meant sitting and watching a progress ring, even though the offline path already knew how to queue the message and let the outbox drain finish the upload later. Reported from the field: a video at 60% with a greyed-out button.
Fix
composerAttachmentUploadBlockReasonnow only blocks on a failed upload, since the outbox drain would hit the same failure and the user has to retry or remove the file first. An upload still in flight no longer blocks: the message queues, and the drain'sprepareQueuedMessageAttachmentsreuses the finished upload (or re-sends the local bytes if the pending upload expired).handleStarttakes the outbox branch instead of callingcreateProjectThread. The button switches to the tray icon and its accessibility label reads "Queue task, sends when uploads finish". The worktree-setup shimmer no longer shows for a queued task.composerAttachmentsStillUploadingfor the label logic, alongside the (now narrower) block reason.Verification
apps/mobile:vp test run src/lib/composerAttachmentUploadQueue.test.ts(9 passed; the block-reason test now pins that in-flight does not block and failed does).vpr typecheckpasses. Lint on touched files shows only pre-existing warnings (15, same as base).Before / after — new-task composer with a 45 MB video mid-upload
After tapping Queue mid-upload
The task lands in the Unsent section as "Sends on reconnect". About 75 seconds later the drain delivered it; the server's projection shows the created thread, the user message with the 47 MB file attachment claimed, and the agent's reply.
Captured on an iPhone 17 Pro simulator against a disposable backend behind a 150 KB/s throttling proxy so the upload was slow enough to observe; the "before" is
mainwith the same seeded draft.Surfaces
Model: Claude Fable 5 · Harness: Claude Code in T3 Code
🤖 Generated with Claude Code