Found during adversarial review of #8237 adoption (PR for upstream/2026-08-31-mobile-files). Upstream design, adopted as-is; filing rather than folding into that PR to keep it to one concern.
Problem
apps/mobile/src/features/sharing/incoming-share-storage.ts writes one JSON per share with no count cap, no size cap, and no expiry. Nothing prunes the directory — INCOMING_SHARE_DIRECTORY appears in that file only.
removeIncomingShareDraft is reachable only via IncomingShareInbox.consume, whose sole production caller is NewTaskDraftScreen.tsx:534 — on a successful import. Dismissing the sheet latches dismissedShareId (incoming-share-presentation.ts:44-49) and leaves the draft on disk permanently.
Only drafts[0] is ever surfaced (IncomingShareProvider.tsx:324). There is no inbox list, so anything at index >= 1 is unreachable while the top item is merely dismissed.
Why it matters more now
Before file attachments, an image share stored base64 in the JSON, bounded at 8 x 10 MB. Now each share can additionally copy up to 8 x 50 MB into Documents/t3-composer-attachments/ — the non-evictable, iCloud-backed directory, not the cache.
Share and dismiss 20 files: roughly 1 GB, invisible in-app, unreclaimable without reinstalling.
Rule this violates
AGENTS.md "Reverse states": If you added a way in, add the way out and the way to see it. A one-way door is a bug.
Sketch
Any of: a retention cap or TTL with a pruner; deleting the draft on dismiss rather than latching it; or an inbox list so items behind drafts[0] are reachable.
Found during adversarial review of #8237 adoption (PR for
upstream/2026-08-31-mobile-files). Upstream design, adopted as-is; filing rather than folding into that PR to keep it to one concern.Problem
apps/mobile/src/features/sharing/incoming-share-storage.tswrites one JSON per share with no count cap, no size cap, and no expiry. Nothing prunes the directory —INCOMING_SHARE_DIRECTORYappears in that file only.removeIncomingShareDraftis reachable only viaIncomingShareInbox.consume, whose sole production caller isNewTaskDraftScreen.tsx:534— on a successful import. Dismissing the sheet latchesdismissedShareId(incoming-share-presentation.ts:44-49) and leaves the draft on disk permanently.Only
drafts[0]is ever surfaced (IncomingShareProvider.tsx:324). There is no inbox list, so anything at index >= 1 is unreachable while the top item is merely dismissed.Why it matters more now
Before file attachments, an image share stored base64 in the JSON, bounded at 8 x 10 MB. Now each share can additionally copy up to 8 x 50 MB into
Documents/t3-composer-attachments/— the non-evictable, iCloud-backed directory, not the cache.Share and dismiss 20 files: roughly 1 GB, invisible in-app, unreclaimable without reinstalling.
Rule this violates
AGENTS.md "Reverse states": If you added a way in, add the way out and the way to see it. A one-way door is a bug.
Sketch
Any of: a retention cap or TTL with a pruner; deleting the draft on dismiss rather than latching it; or an inbox list so items behind
drafts[0]are reachable.