Repository navigation
bug: a file attached to an unsent Workspace message cannot be removed from the composer #3738
Description
Summary
A file attached to a Workspace chat message that has not been sent yet cannot be removed. The pending chip above the composer shows the file name and its upload state, but it has no × and no other way to take the file off the message: the only ways out are to send it anyway or to leave the chat. The same gap exists in the room composer.
Context
Reported from the live Workspace on the 1.0 rc line (dev @ e4e739f7). A dropped, pasted or picked file is uploaded to the agent's inbox at once (ent#524) and its chip rides the composer until Send; a pending chip that cannot be dismissed means a wrong file goes to the agent with the message. The drop composable (src/frontend/src/composables/usePortalFileDrop.js) already exposes a per-entry remove function, and the chip's own padding reserves room for a trailing control, but neither PortalConversation.vue nor PortalRoom.vue wires it.
Acceptance Criteria
- Every pending attachment chip in the Workspace chat composer carries an × that removes that file from the outgoing message, with an accessible name that says which file it removes.
- Clicking × never submits the composer and never selects or focuses anything else; the draft text and the other chips are untouched.
- × works in every chip state (queued, uploading, uploaded, failed). Removing an uploading or uploaded file also deletes its uploaded copy from the agent's inbox when that upload had completed, best effort: a delete failure does not block the removal and is not shown as a chip error.
- Removing the last chip hides the chip row and lifts any send-blocking state that existed only because attachments were pending (for example the discussion-answer block that attachments impose).
- The room composer (
PortalRoom.vue) gets the same affordance with the same behaviour. - The × follows the design system: the chip's own ink (
text-current), a visible hover and focus-visible state, large enough to hit on touch, no new raw colours. - jsdom mount coverage in
src/frontend/tests/unit/: a chip renders the ×; clicking it removes exactly that entry and calls the inbox delete only for a completed upload; the composer does not submit.
Technical Notes
src/frontend/src/composables/usePortalFileDrop.js— the entries list andremoveAt;clearresets the batch notice too.src/frontend/src/components/portal/PortalConversation.vue— the pending chip row (data-testid="portal-attachment-chip") and the composable destructure;discussionAnswerBlockedreadsattachments.length.src/frontend/src/components/portal/PortalRoom.vue— the room's chip row (data-testid="portal-room-attachment-chip").src/frontend/src/stores/clientPortal.js—deleteUpload(agentName, filename).- Existing composer specs:
src/frontend/tests/unit/portalComposerWiring.spec.js,roomComposerChain.spec.js.
Activity
feat(workspace): remove a pending attachment from the composer — chip…
feat(workspace): room composer uses the shared attachment chip with × (…
fix(workspace): attachment chip × — same-name skip ignores failed chi…
vybe commentedon Oct 14, 2026
Draft PR: #3743 — stacked on #3742 (base
feature/3737-overflow-starred-row), tip 099c59c. Review: MERGEABLE; two informational findings fixed, one recorded; cso clean. Merges after #3742, retargeted todev.test(workspace): attachment chip × — executing mount test for the esc…
vybe commentedon Oct 14, 2026
/validate-pron #3743, round 2: APPROVE (review by trinity-ability 2026-10-14 22:20Z on tip a4a22da; 203/203 targeted tests; the round-1 blocker — no executing test for the escalation hand-off lock — closed byportalAttachmentChipHandoff.mount.spec.js, all three lock mutations now red). PR marked ready for review. Merge order: #3742 first, then retarget #3743 todevand let the dev-only checks (Analyze ×2, backend-unit-test) run before merging.feat(workspace): remove a pending attachment from the composer with a…