Repository navigation
fix(mobile): keep composer attachment counts and names within bounds - #8569
Conversation
Attachment additions reserve their capacity before any asynchronous work, and the commit recomputes the remaining room from the live chip list. Uploads start only for the chips that were kept, and a selection that does not fit gets the existing truncated-selection feedback. App-owned cache files of dropped candidates are released. The attachment ref advances before React renders, so the room a commit reads and the list it appends to are always the same list. Chip names and the submission's original name are composed through the filename normalizer, which keeps a single safe file segment within the byte budget. restoreChips is bounded like every other append.
Attachment additions reserve their capacity before any asynchronous work, and the commit recomputes the remaining room from the live chip list. Uploads start only for the chips that were kept, and a selection that does not fit gets the existing truncated-selection feedback. App-owned cache files of dropped candidates are released. The attachment ref advances before React renders, so the room a commit reads and the list it appends to are always the same list. Chip names and the submission's original name are composed through the filename normalizer, which keeps a single safe file segment within the byte budget. restoreChips is bounded like every other append.
|
bot: Android runtime captures from the local dev stack (fake account, non-production data). The composer was given six documents in one picker selection. The first capture shows the strip and the truncated-selection message the composer produced. The second shows the end of the strip, where the remaining kept chips sit. Across the strip, Chips show a retryable upload state afterwards because this stack mocks the object-storage credentials; the PUT leg cannot succeed locally. Admission, chip count, and the message are what these captures show. |
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Executive SummaryThe incremental change moves the full-composer toast into a shared Assumptions: Files Reviewed (2 files)
Previous Review Summary (commit f21003c)Current summary above is authoritative. Previous snapshots are kept for context only. Previous review (commit f21003c)Status: 1 Issue Found | Recommendation: Address before merge Assumptions: Overview
Issue Details (click to expand)SUGGESTION
Files Reviewed (2 files)
Reviewed by deepseek-flash-latest · Input: 30.8K · Output: 3.7K · Cached: 284.8K Review guidance: REVIEW.md from base branch |
When the strip reaches its limit while an add is still measuring, the add now announces the full-composer message instead of returning without feedback. The message lives in one helper used by both capacity paths.
|
bot: Local Android dev-client run of the composer attachment flow on this branch. Captures show the chip row at the configured limit, the composer's status notice, the attach control reporting disabled at the limit, and one more file accepted after a chip was removed. Uploads settle into the local upload-error state because the local stack has no object storage, which does not affect the chip count or the upload-slot request accounting. |
eshurakov
left a comment
There was a problem hiding this comment.
Shallow review: no critical issues found.






Evidence
Evidence: demonstrated — on a local dev stack with the Android dev client, signed in locally, one picker selection larger than the composer's file limit produced only the allowed number of attachment chips, showed the composer's existing excess-selection notice, and issued an upload-slot request only for the chips that were kept. The attach control then reported disabled at the limit, re-enabled after a chip was removed, and accepted one more file with a single new upload-slot request. Separately, with a first selection still being processed, a second selection was admitted only up to the remaining capacity: the chip row finished at the limit, the composer showed its maximum-files notice, and the backend recorded upload-slot requests for the first selection only. Chip labels came from the shared filename normalization on the real surface.
Change
The mobile composer keeps its attachment count and chip names within the configured bounds: capacity is claimed before the asynchronous measurement work and re-checked against the live chip list at commit, uploads start only for chips that are kept, restored chips obey the same limit, and selected and restored names go through the shared filename normalization.
Verification
enabled="false"; after removing one chip it reportsenabled="true", and one further file is accepted, restoring the limit with exactly one new upload-slot request.pnpm -C apps/mobile exec vitest run src/lib/agent-attachments) cover those orderings directly, including names carrying separators and dot segments.Production prerequisites
None.
Proxy answers
The owner's proxy gave these answers in the owner's place.
commitAttachmentscurrently writesattachmentsRefinside a React functional updater, so the ref only advances when React flushes. The clamp needs a synchronous, authoritative count.Answer: Ref becomes authoritative
Confidence: 0.75 · Precedent: Autonomy: fix the root cause and the class of the issue — "fix class of issue preventing progress"
restoreChipsre-adds chips after a failed optimistic send (normally into an empty list) and today appends without a cap. When the cap would be exceeded, what should it do?Answer: Clamp silently
Confidence: 0.55 · Precedent: Speed: do the least work possible with no drop in quality — "do the least amount of work possible, with no drop in quality"
Answer: Sanitize the picker name first, apply the normalized extension, then run the sanitizer once more on the result. A bare dot segment becomes file.bin, and the stored value still passes through the sanitizer and stays within the byte budget. Add a test for
.,.., and an over-long name.Confidence: 0.50 · Precedent: Autonomy: fix the root cause and the class of the issue — "if anything repeats the same mistake over and over again, fix the class"
Answer: Chip name + restored fallback
Confidence: 0.85 · Precedent: Autonomy: fix every affected surface — "fix this for all shared services model pickers on all surfaces"
Answer: Temporary local routing
Confidence: 0.74 · Precedent: Speed and least work: do not let spend caps or quota stop progress; Autonomy: blocked is not an acceptable end state and narrowly scoped unblocking changes are approved