Repository navigation
Description
Out of scope
No picker implementation, PDF extractor/reader selection, general plugin registry, relaxed track parser, uploaded-code execution, automatic operational inbox work, automatic canonical filing or implied Undo feature. Content-reading strategy belongs to #3154 and interaction design to #3155; #3151 supplies the concrete reconciled design.
Context
Part of #3150.
Blocked by #3151
stageShareAt (packages/ui-server/src/share/staging.ts:298-424) already retains arbitrary file bytes and validates tracks. resolveChatFiles (packages/ui-server/src/tracks/read.ts:58-63) still requires every chat file to parse as a track; withTrackFiles at lines 69-72 emits track-specific context. createShareRoutes (packages/ui-server/src/routes/share.ts:108-222) gives /share operational intake side effects while /track-upload is track-specific. Current dispatch combines file/image limits at packages/ui-server/src/ws/dispatch.ts:133-149. Image model content alone is not proof that an original filesystem file is retained.
The maintainer's November 2 review selects the neutral route, filesystem originals, durable draft/chat lifetime, aligned byte/count envelope, protected-reference cleanup and configurable total attachment pool below. These supersede the October 29 deferral for those policy questions. #3151 remains an actual design dependency, not an unanswered choice between those branches.
Scope
Implement a separate neutral composer-upload endpoint with concrete type routing, reusing staging/validation internals. Bind references against authenticated server-owned original files and recorded ownership; keep images and validated tracks specialized without forcing every other file through track parsing. Implement the selected durable ownership, admission and cleanup semantics and preserve existing share/track callers or document an explicitly compatible migration.
Acceptance criteria
-
Implement the G1 A snapshot list below: the draft's
outgoingis a list of up to 10{ snapshotId, requestId, text, fileIds, state, createdAt }items per chat; the creating save counts every live snapshot for the chat across tabs and devices in the same transaction and returns409 DRAFT_OUTGOING_FULLbefore any send effect; Cancel, upload failure, dispatch and non-acceptance transitions edit in place without a free slot; release only on confirmed acceptance, Edit or confirmed Delete…; every live snapshot is in aggregate accounting. Tests prove ten combined slots, eleventh refused with nochat_messageeffect, Cancel at 10 of 10, two devices at 9 each racing and exactly one admitted, and a mutation counting only one tab's snapshots failing. The single-outgoing-slot implementation does not satisfy this criterion; a shipped single-slot field needs additive compatibility treatment. -
Generic PDF, text, ordinary JSON and unknown binary originals within the selected envelope can upload and bind to a draft/chat without unsupported-track rejection. Unknown type alone is not an admission failure, and upload never claims successful extraction.
-
Every attachment including images has real original filesystem bytes and an authorized stable read/reference path inside the agent environment. Prove actual reads/digests with image, PDF/text and track fixtures; metadata-only/inline-only assertions do not qualify. Retained originals remain available to later conversation turns.
-
Server-owned reference resolution rejects missing/stale/altered/cross-owner/symlink/outside-authorized-store references and client authority overrides. Revalidate recipient/taint and filesystem access constraints; attaching a file is no arbitrary-path or execution grant.
-
Content-valid GPX/KML/GeoJSON preserve the existing shared parser's metrics, partial/omission evidence and rendering metadata. Wrong extensions/MIME cannot forge track evidence; ordinary JSON remains generic. Original preservation does not remove separate specialized processing bounds.
-
Enforce 10 attachments, 25,000,000 original bytes each and 50,000,000 combined original bytes per draft/message, including images. Preserve the 65,536-byte text allowance separately and update the old draft combined-byte accounting/advertised limits consistently. Streamed and actual sizes, not client claims, enforce admission.
-
Implement the configurable default 1 GiB total attachment pool across draft/sent/pending/grace storage with durable cross-operation accounting/reservations. Preserve the 100-draft/256-MiB draft defaults as sub-limits. Pending and derived objects cannot escape accounting. At capacity refuse explicitly, preserving existing referenced/in-grace content; concurrent admissions cannot multiply the cap.
-
Exact acknowledged draft revisions and filesystem bindings survive reload/tab close/normal restart. An accepted send retains conversation references before consuming its submitted draft revision; newer edits, other drafts and unresolved sends remain intact. Recovery/idempotency prevents duplicate files, cross-draft binding and premature cleanup; no automatic resend or falsely saved state.
-
Cleanup uses one hour for never-bound uploads and seven days from authoritative last-reference removal for formerly referenced originals, with clocks/eligibility retained across restart. Recheck every reference, live operation and file identity at deletion under race-safe coordination. Uncertain sends/ownership/state protect files. Rebinding cancels collection; no mtime reset, early quota-driven purge or implied Undo.
-
The new route creates no operational inbox/queue/triage job. Preserve existing share/track intake behavior and the settled mobile source shortcuts. Concrete schemas, capability negotiation and bounded detection/resource implementation must match ui-server: design generic attachment intake, routing and content access #3151's design under the selected policies, without introducing a public registry.
-
Update runtime schemas and authoritative HTTP/WS/package/draft/storage contract documentation, API reports where relevant and minor changesets. Record the D52 lifetime/envelope amendment, pool and cleanup policies and rejected alternatives in the relevant decision records in this implementation. Preserve legacy/unavailable-host honesty and existing unrelated draft behavior; no retrospective saved claims.
Verification
Extend share-routes, track-intake, ws-track-files and draft/reference/storage tests with real chat dispatch and filesystem reads. Cover mixed files, tampered authority/metadata, misleading MIME, generic versus valid-track routing, original/derived limits, concurrent quota admission, ownership, acknowledged save/reload/restart, accepted/uncertain send, version edits, reference transfer, last-reference cleanup and rebind/unlink races. Use keyless offline fixtures and actual retained bytes; no paid service.
Run bun run test packages/ui-server/tests/share-routes.test.ts packages/ui-server/tests/track-intake.test.ts packages/ui-server/tests/ws-track-files.test.ts plus focused new ownership/quota/draft suites, typecheck and bun run check:pr --base origin/main. Retain a failing-first generic/PDF admission witness and restored mutations for authorization, original-file presence, quota and cleanup protections on their intended assertions. Tests of turn-path/provider context changes run the append-only history guard; do not mutate previous provider tools/system/messages.
Contract impact
A new composer endpoint, server-owned references, generic durable-draft capability, advertised limits and configurable attachment-pool input need contract labeling, CONTRACT-prefixed implementation commits, authoritative same-commit docs and appropriate minor changesets. Preserve old supported share/track shapes and existing client behavior through additive negotiated evolution where possible. Under G1 A this includes the draft outgoing snapshot list and the new 409 DRAFT_OUTGOING_FULL refusal in the draft-save HTTP contract; if a single-slot outgoing field has already shipped on main, the list needs additive compatibility treatment or its own breaking ruling. The explicit lifetime and limit rulings authorize their named amendments; any additional breaking retype/removal or changed existing-source meaning needs its own ruling before implementation. No new provider transfer or release milestone is approved.
Approved decisions
Approved shared constraints (maintainer, 2026-11-02; #3153)
- Admission: separate neutral composer-upload endpoint reusing concrete staging internals, with no operational inbox/queue/triage side effects. Existing share/track callers retain their documented behavior; preserve the settled phone Photo library/Camera/unrestricted Files entry points and common processing pipeline.
- Actual files: every attachment, including images, has a byte-identical uploaded-original filesystem file and authorized stable reference readable from the agent's execution environment. Inline/base64 context, previews or extracted text alone are insufficient. Preserve specialized processing, taint/recipient/permission rules and honest content-access outcomes; a stored binary is not automatically semantically readable.
- Ownership/lifetime: originals belong to the exact acknowledged draft revision and, after accepted send, the retained conversation. A saved draft including its file bindings survives reload/tab close and normal host restart. Sending retains originals for later agent reads without consuming newer edits or another draft. No automatic filing into canonical brain content. Unacknowledged/legacy data never falsely reads as saved; unresolved sends protect their files. This explicitly amends D52's tab-only staged-track exception for the new generic path.
- Per-draft/message envelope: 10 attachments total, 25,000,000 original bytes per file and 50,000,000 original bytes combined, including images. Preserve the 65,536-byte text allowance separately and reconcile the old 8,388,608-byte draft ceiling so an eligible original batch can be saved subject to aggregate admission. Original-byte limits do not relax separate image/provider-frame/track-parser bounds or authorize silent omission.
- Aggregate storage: a configurable host attachment-pool cap, default 1,073,741,824 bytes (1 GiB), covers draft, sent, pending and grace-period attachments. Account materialized originals/derived attachment storage and in-flight reservations under bounded accounting; do not leave an uncharged derivative or pending-file escape. Preserve the 100-live-draft default and 268,435,456-byte host draft sub-limit. Draft storage is a subset of the attachment pool, not an additional 256 MiB on top. Shared physical storage/reference accounting must be explicit. At capacity refuse new work honestly and preserve existing references; no early purge to make room.
- Cleanup: never-bound uploads have a one-hour window; formerly referenced originals have seven days from authoritative last-reference removal. Saved draft/chat references, active work and unresolved sends protect files. Revalidate ownership, identity and all references immediately before deletion, coordinating rebinding against unlink. Unknown reference state fails closed. Rebinding cancels collection; a later last-reference removal starts a new seven-day interval. Incidental access/mtime does not reset it. Grace-period storage remains charged. No implied Undo feature or permission to delete unrelated canonical/share data.
- Design/compatibility: ui-server: design generic attachment intake, routing and content access #3151 must turn these settled constraints into exact reference/route/capability schemas, durable ownership/admission accounting, detection/resource bounds and executable proof briefs. Record choices and rejected alternatives in D52/attachment decision guidance and authoritative contract docs. Concrete names and internal implementation details may be chosen within these constraints; a proposal exceeding them requires a new ruling. No release commitment is made.
Readiness and design dependency
The maintainer policy questions on this admission issue are answered. Remove needs: decision and add agent-ready because no other needs label remains. Retain blocked on #3151: implementation must consume its concrete route/reference/resource/compatibility design rather than inventing or bypassing that deliverable. Reader and UI decisions remain on #3154/#3155; this readiness does not complete those issues or approve their unreviewed choices.
2026-11-02 decision review: consolidated the selected admission/original/lifetime/limit/cleanup/pool policies into checkable criteria and replaced the original deferred alternatives. No implementation, live upload/deletion, proof completion or release commitment is claimed.
Blocked by #3291
Later recovery-capacity amendment — maintainer, 2026-11-02 (#3155)
The subsequent interactive #3155 review selects multiple separate recovery cards, with up to 10 pending/recoverable snapshots combined per chat, rather than a single outgoing slot. Count held, returned and dispatched-but-unconfirmed snapshots together across tabs/devices; each keeps its own exact text/file bindings and identity. Confirmed accepted sends leave the queue, retaining their conversation references. This supersedes only the earlier single-slot restriction (including #3151's approval of §13 item 6); the other five approved recommendations and existing ownership/lifetime/security policies remain in force.
Admit/reserve capacity before a new send creates effects. At capacity explain why new sends wait, while drafting, checking/recovery and cancellation remain available. Cancelling admitted work must preserve that same snapshot without requiring another slot. Do not merge/overwrite/evict unresolved snapshots, automatically replay uncertain sends or authorize parallel provider turns. Keep per-message envelopes and aggregate 256-MiB draft/1-GiB attachment-pool defaults unchanged; the count does not guarantee byte capacity or multiply those allowances.
Reconcile the design's single outgoing representation, contracts and reference transfer/accounting with multiple independently identified snapshots before claiming the affected scope complete. Preserve each snapshot across newer edits, acknowledgement loss, reload, auth/session changes and uncertain sends; release/collect only after the matching authoritative ownership transition. The durable records remain app-owned internal state, outside provider history. Concrete API names/representation and compatibility treatment remain the design/delivery owners' responsibility; no new public persistence seam is approved.
G1 A — snapshot list (maintainer, 2026-11-03; #3155). The draft's single outgoing? field (docs/plans/chat-attachments.md §6.4) becomes a list of up to 10 snapshots per chat, each { snapshotId, requestId, text, fileIds, state: held | returned | dispatched, createdAt }, keyed for ownership by the existing §6.3 draftId:requestId refs (no new table). The save that creates a snapshot counts every live snapshot for that chat across tabs and devices in the same transaction and refuses the eleventh with 409 DRAFT_OUTGOING_FULL before any send effect; that save is the reservation. Cancel (held → returned), upload failure (held → returned), dispatch (held → dispatched) and a recovery read showing non-acceptance (dispatched → returned) edit a snapshot in place and never need a free slot. A slot is released only on confirmed acceptance (refs move to the message), Edit into an empty composer revision or confirmed Delete…; nothing is evicted, merged or overwritten. An ordinary all-ready send also creates a dispatched snapshot until acceptance. Source: #3155 design brief rev 5 §2. Rejected: a separate send-intent table.
Add explicit proof for ten combined slots, eleventh-send refusal before effects, cancellation while full, independent per-card recovery across reload and concurrent devices, older acknowledgements not consuming newer drafts, no duplicate resend and no early file cleanup. Reconcile the final UI matrix under #3155's remaining needs: design; the selected product behavior itself needs no repeat interview.
2026-11-02 decision propagation: recorded the later #3155 E2 B / capacity B / E1 A rulings in the current brief. Initial single-slot design delivery is not evidence this amendment is implemented or verified. Preserve assignments, existing blockers and release commitments; no PR/runtime completion is claimed.
Triage 2026-11-03: folded the maintainer ruling (#3155 G1 A, snapshot list with 409 DRAFT_OUTGOING_FULL in the save transaction) into the first acceptance criterion, the recovery-capacity amendment and contract impact.
Activity
schlessera commentedon Oct 29, 2026
Decision (maintainer, 2026-10-29): Selected option 1: wait for #3151's upload/lifecycle design before selecting the generic composer admission route. Neither a neutral composer-upload path reusing staging nor an adapted share path is selected ahead of that design.
#3151 must specify route/schema, server-owned reference ownership/lifetime, count/byte/detection bounds and compatibility before they are folded into this brief. Keep #3153 blocked on #3151 with
needs: decisionfor those substantive choices and noagent-ready. Preserve the approved generic-file outcome, phone Photo library/Camera/unrestricted Files shortcuts, specialized image/validated-track processing and separation from unintended operational inbox work. No runtime implementation, compatibility break, provider transfer or release commitment is approved by this deferral.schlessera commentedon Nov 2, 2026
Decision (maintainer, 2026-11-02): Selected A: use a separate neutral composer-upload endpoint, reusing staging internals. Do not adapt operational /share with a composer mode or create inbox/queue/triage work as a side effect of attachment admission. This supersedes the October 29 deferral for the upload-path choice only.
Additional requirement: Every chat attachment, including images, must exist as an actual filesystem file containing the uploaded original bytes, not solely inline/base64 model context, a preview or extracted text. The agent must receive an authorized stable reference that resolves to that file inside its execution environment and can read/refer to it using appropriate tools. Preserve specialized image/track processing; derived provider representations do not replace the retained original. Server-owned identity/ownership and existing sandbox, taint and recipient rules still apply. This does not promise that every binary can be semantically decoded. Retention duration, unsent recovery, send ownership, cleanup and exact resource limits remain separate decisions; filesystem existence alone is not a permanent-filing or saved-draft promise.
The body and acceptance criteria now require real authorized filesystem reads of retained originals, including images, rather than metadata or inline-only context. The shared requirement is also folded into #3150/#3151/#3154/#3155; their other decisions remain open. Keep needs: decision and blocked until lifetime, ownership, exact bounds and the reconciled reference/schema decisions are settled.
schlessera commentedon Nov 2, 2026
Decision (maintainer, 2026-11-02): Selected A for attachment-original lifetime: draft/chat-owned files rather than automatically filing every upload into canonical brain content.
Selected A: originals are owned by the draft and, after accepted send, by the conversation. A host-acknowledged exact draft revision, including its filesystem attachment bindings, survives reload/tab close and normal host restart; restore it only for the authorized owner/devices under the existing host/root identity. Merely completing an upload or saving text is not enough to claim the entire draft saved. Accepted send transfers or adds the durable conversation reference without deleting the original when the submitted draft revision is consumed. Later turns can read/reference the same original through an authorized stable filesystem reference. Preserve edited/newer draft revisions and held/uncertain sends: no premature deletion, cross-draft rebinding or automatic resend. Do not automatically file these originals as canonical brain content. A live draft, retained conversation or unresolved accepted-send outcome protects its files from staging TTL cleanup. Removal of an owning reference is not permission to delete files still referenced elsewhere; detailed unreferenced-file cleanup, storage admission and exact limits remain to be specified. Never silently purge referenced files to make room.
This explicitly amends D52's tab-only staged-track exception for the new generic path. Record the acknowledged-recovery/ownership amendment and alternatives in docs/decisions/design-kit/sessions-and-drafts.md and authoritative contract documentation during implementation; legacy or unacknowledged data must not falsely read as saved.
The body now contains the shared lifetime choice. Next reconcile upload versus draft/host resource limits and cleanup protection; their exact values/policy are not selected here. Preserve existing labels/dependencies pending the rest of each issue's review. No implementation or release commitment is claimed.
schlessera commentedon Nov 2, 2026
Decision (maintainer, 2026-11-02): Selected A: expand durable draft attachment capacity to match the existing shared-upload limits rather than constrain generic batches to the old 8 MiB draft ceiling.
Selected A: align durable draft attachment capacity with the existing shared-upload envelope: at most 10 attachments total, 25,000,000 original bytes per file, and 50,000,000 original bytes combined per draft/message. These are decimal byte limits and include image originals; they are not an extra allowance per file type. Retain the existing 65,536-byte draft text allowance separately, so an otherwise eligible full-size original batch is not unsaveable merely because of the old 8,388,608-byte combined draft ceiling. Update the public draft limit/accounting contract to describe original bytes, text and bounded derived representations accurately. Existing image-transformation/provider-frame and track-parser bounds remain separate; generic admission does not assert that a specialized processor can handle the entire original. Never drop originals or silently claim processed/saved status to fit a lower processing limit. Preserve the existing 100 live draft default and 268,435,456-byte host draft pool limit, charging retained/pending bytes according to explicit bounded accounting. An attachment within per-file/per-draft limits may still be refused when aggregate capacity is full; report the reason and preserve existing referenced content. No increase to host-wide capacity, sent-file retention quota, processing/context budgets or unreferenced-cleanup policy is selected by this upload/draft-envelope ruling.
The body now incorporates this selected envelope. Reconcile the public draft limit/accounting fields and migration in #3151 and the implementation; do not claim every within-envelope upload is saved if host admission refuses it. The existing draft text/count/host-pool defaults are preserved. Other unresolved policy/design choices and labels remain unchanged; no implementation or release commitment is claimed.
schlessera commentedon Nov 2, 2026
Decision (maintainer, 2026-11-02): Selected A for unreferenced-original cleanup: one hour for uploads never bound to a draft/chat, and seven days after authoritative removal of the last reference for formerly referenced originals.
Selected A: never-bound uploads retain the existing one-hour cleanup window; originals that previously had an owning draft/chat reference retain a seven-day grace period starting when the last reference is authoritatively removed. Referenced saved drafts, retained conversations, unresolved sends and active operations protect their files. Lost acknowledgements, ambiguous ownership, unavailable reference state or uncertain cleanup are not proof that a file is unreferenced. Recheck identity, ownership, active work and all references immediately before deletion; coordinate rebinding and cleanup so a live reference cannot race the unlink. If the file becomes referenced again, cancel its unreferenced eligibility; a later last-reference removal starts a new seven-day interval. Do not reset the clock from incidental access or file mtime. No early purge of referenced or still-in-grace originals to admit a new upload. Refuse new storage with an honest reason when necessary. The grace period alone promises no Undo UI or automatic recovery feature, and this ruling does not authorize deleting canonical brain assets or unrelated share/intake stores.
The body now contains the exact cleanup clocks and safety boundary. Immediate deletion and manual-only cleanup are not selected. Record these semantics in the attachment/D52 decision and authoritative contract during implementation. Other outstanding admission/design choices and labels remain unchanged; no actual deletion or release commitment is claimed.
1 remaining item
schlessera commentedon Nov 2, 2026
#3151 design available for review (PR #3301, base
epic/3150-attachments). It is not merged, and the recommendations in its §13 are not rulings yet. What this brief consumes:POST /api/drafts/:draftId/filesand theAttachmentFileschema;.brain-ui/attachments/<fileId>/with digest checks, a governed read-only mount and thechat_attachmenttaint area;GET /api/attachments/:fileIdfor originals over the 10 MiB raw-file cap;<brain-attachments>block;Labels are unchanged.
Blocked by #3151remains until that design lands on main with the epic batch.schlessera commentedon Nov 2, 2026
Claimed for implementation (token
claim-3153-1793642870). Branchfeat/3153-generic-chat-file-admissionfromepic/3150-attachmentsat 4cd1985 (design #3301). PR will target the epic branch.fix(ui-server): refuse an upload body longer than its declared length (…
schlessera commentedon Nov 2, 2026
Implementation PR #3342 targets
epic/3150-attachments. Headb2c0cbcdd83eaab42e6d14973d50d071f95a2a92, proof base4cd19855219c8bcdfafb8a1104161a90093c5796. The PR is ready, and hosted ready-candidate confirmation was requested (CI run 41505209464).Local proof:
check:prpasses against the epic base, there is a failing-first PDF witness, and nine mutation receipts are recorded in the PR.Still open, as named in the PR:
releaseSession(that flow is ui-react: design own-chat deletion and targeted cache retirement #3231)The issue stays open with
Blocked by #3151andblockeduntil the epic lands on main. Milestone 0.45.0 is set on this issue and on the PR. The assignment stays while the PR is live.CONTRACT: docs(api): classify the composer file routes in the HTTP in…
schlessera commentedon Nov 2, 2026
Decision amendment (maintainer, 2026-11-02; #3155): Multiple separate recovery cards are selected, with up to 10 held/returned/unconfirmed snapshots combined per chat. This replaces the earlier single-outgoing-slot restriction only; other approved policies remain.
Updated the body and acceptance criteria with the exact admission, cancellation, ownership/accounting and proof requirements. Reserve capacity before new-send effects; at capacity keep drafting/recovery/Cancel available, and never overwrite unresolved work. Existing storage limits still apply. Reconcile the single-slot design/contract before claiming affected delivery complete; no implementation or proof is claimed by this tracker update. See #3155 for the recorded E2/capacity/E1 decisions and final design handoff. Existing assignments, labels, blockers and release commitments are preserved.
schlessera commentedon Nov 3, 2026
Decision (maintainer, 2026-11-03), ruled on #3155 G1: A. The draft's
outgoingis a list of up to 10 snapshots per chat rather than one optional field. The creating save counts live snapshots across tabs and devices in the same transaction and returns409 DRAFT_OUTGOING_FULLbefore any send effect. Rejected: a separate send-intent table.What this means for this issue: the first acceptance criterion and the contract impact now name the list, the 409 and the proof cases. Open PR #3342 currently adds a single
outgoing?: DraftOutgoing | null, so it does not yet meet that criterion. The list has to land in #3342 or in a follow-up before this issue closes. Labels unchanged:agent-ready, withblockedon #3151 and #3291.schlessera commentedon Nov 4, 2026
Unblocked: #3151, #3291 are closed, so
scripts/sync-project.tsis removing theblockedlabel.CONTRACT: feat(ui-server): admit generic chat files as durable server…
CONTRACT: feat(ui-server): read PDF and text attachments on demand th…
CONTRACT: feat(ui-server): keep up to ten outgoing draft snapshots pe…