Thanks to visit codestin.com
Credit goes to github.com

Skip to content

feat: evict the oldest attachments at the chat file cap - #29063

Merged
ethanndickson merged 8 commits into
mainfrom
file-upload-5wrm
Sep 10, 2026
Merged

feat: evict the oldest attachments at the chat file cap#29063
ethanndickson merged 8 commits into
mainfrom
file-upload-5wrm

Conversation

@ethanndickson

@ethanndickson ethanndickson commented Sep 8, 2026

Copy link
Copy Markdown
Member

Closes CODAGT-933

Long agent sessions were hitting the 50 attachment cap, after which every upload, screenshot and attach_file call was rejected, so people were abandoning otherwise healthy chats.

Rather than raise the cap (which just moves the cliff and multiplies the worst-case per-chat bytea storage), a chat now keeps its 50 most recent attachments. LinkChatFilesAfterLock deletes the oldest chat_files rows in the same statement and the cascade drops their links. Files in the incoming batch are never evicted, so the only remaining rejection is a single message with more than 50 files.

Nothing in the product ever linked one file to two chats, so rather than guard the eviction against it, this adds UNIQUE (file_id) on chat_file_links, with the migration dropping any existing duplicates in favour of the oldest chat.

Evicted attachments render as the existing "Attachment expired" tile. A pinned flag is the obvious follow-up if someone needs to keep a specific file around.

@linear-code

linear-code Bot commented Sep 8, 2026

Copy link
Copy Markdown

CODAGT-933

@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Docs preview

Check off each page once it's been reviewed. If a page changes in a later push, its checkbox clears automatically so it gets a fresh look. Pages not yet wired into the docs navigation aren't listed here.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 8, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-10T02:58:36.739580Z 6c5de89 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@coderagents

coderagents Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Documentation Check

Updates Needed

  • docs/ai-coder/agents/index.md (File attachments section) - Addressed. The wording now reads "A conversation keeps its 50 most recent attachments, and older attachments are removed automatically," which correctly reflects the new eviction behavior.

Automated review via Coder Agents

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bcbe2deda3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread coderd/database/queries/chats.sql
Comment thread coderd/database/queries/chats.sql Outdated
@ethanndickson

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2a3369b469

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@ethanndickson

Copy link
Copy Markdown
Member Author

@codex review

re: Lock link writes before deduplicating

We don't support rolling upgrades, so this isn't a case we need to handle. No other migration in the repo guards against writes from an old replica mid-migration, and the upgrade docs recommend scaling to zero before upgrading. Between the DELETE and the ALTER TABLE there's nothing running that could insert a link.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Hooray!

Reviewed commit: 2a3369b469

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@ethanndickson

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8ef11f7729

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread site/src/pages/AgentsPage/components/ChatConversation/AttachmentBlocks.tsx Outdated
Comment thread site/src/pages/AgentsPage/components/ChatConversation/AttachmentBlocks.tsx Outdated
@ethanndickson

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: db26ee0489

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread coderd/x/chatd/ARCHITECTURE.md
@ethanndickson

Copy link
Copy Markdown
Member Author

@codex review

re: Replace the architecture rewrite with TODO markers

That is the architecture.md change I've made and written, we don't want to commit todo markers to main.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: db26ee0489

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +288 to +289
if (part.type === "file" && part.file_id) {
referencedFileIds.push(part.file_id);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Handle evicted recordings in the attachment fallback

When newer attachments evict a computer-use recording, this scan cannot detect it because recording_file_id and thumbnail_file_id are nested in a wait_agent tool result rather than represented as type: "file" parts. storeRecordingArtifact links those files to the parent chat, so they participate in the new eviction policy, but RecordingPreview will continue rendering an active play button and at most report the thumbnail as unavailable after the video has been deleted. Include recording IDs in the availability model and have the recording renderer show the expired state.

AGENTS.md reference: site/AGENTS.md:L11-L11

Useful? React with 👍 / 👎.

@ethanndickson

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 69d5aa0ef0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread coderd/database/queries/chats.sql
Comment thread coderd/database/queries/chats.sql
@ethanndickson

Copy link
Copy Markdown
Member Author

@codex review

re: Include queued attachments in eviction ordering

The scan only loses its anchor when queued messages alone evict every historical file, which needs 50 or more files across queued messages while the chat is busy. A smaller queued batch on a full chat still flags the evicted files, because the newest surviving historical reference anchors the scan. Once the queued message is promoted its references enter the scan and the state corrects itself, and text attachments still fall back on click via the 404 path. Not worth the extra plumbing for that window.

re: Exclude links belonging only to deleted messages

This is pre-existing. On main those links count towards the cap and cause the attach to be rejected outright. Here they cause one earlier eviction and then age out as newer files arrive, so this branch is strictly better than main for that case. Unlinking on soft delete and queued delete is a reasonable follow-up but out of scope here.

re: Base eviction order on attachment time

Correct, the order is upload time rather than link time. The two differ only when a file is uploaded and linked later, which in the UI means a file sitting in the composer while the agent attaches 49 or more files before the user sends. The worst case is one user file evicted early and re-attached. The window is the 50 most recently uploaded files linked to the chat, and I'm happy with that trade-off rather than adding a timestamp to the link table.

re: Replace the architecture rewrite with TODO markers

That is the architecture.md change I've made and written, we don't want to commit todo markers to main.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 69d5aa0ef0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

};

const displayMessages = buildDisplayMessages(parsedMessages);
const evictedFileIds = deriveEvictedFileIds(parsedMessages, chatFiles);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Refresh chat files after tool-created attachments

When attach_file, propose_plan, or recording storage evicts a file near the end of a run, this derives expiry from stale chatFiles: those tools do not invalidate the chat entity, watch payloads explicitly omit Files, and AgentsPageLayout cancels entity refetches before merging the terminal waiting status, which disables the only 5-second poll. The old attachment therefore remains rendered as available until a focus or manual refetch. Fresh evidence in this revision is that the new derivation now depends entirely on that metadata while the terminal watch path still cannot update it; invalidate the chat entity when an attachment-producing tool completes or when the run settles.

Useful? React with 👍 / 👎.

@ethanndickson

Copy link
Copy Markdown
Member Author

@codex review

re: Include queued attachments in eviction ordering

The scan only loses its anchor when queued messages alone evict every historical file, which needs 50 or more files across queued messages while the chat is busy. A smaller queued batch on a full chat still flags the evicted files, because the newest surviving historical reference anchors the scan. Once the queued message is promoted its references enter the scan and the state corrects itself, and text attachments still fall back on click via the 404 path. Not worth the extra plumbing for that window.

re: Exclude links belonging only to deleted messages

This is pre-existing. On main those links count towards the cap and cause the attach to be rejected outright. Here they cause one earlier eviction and then age out as newer files arrive, so this branch is strictly better than main for that case. Unlinking on soft delete and queued delete is a reasonable follow-up but out of scope here.

re: Base eviction order on attachment time

Correct, the order is upload time rather than link time. The two differ only when a file is uploaded and linked later, which in the UI means a file sitting in the composer while the agent attaches 49 or more files before the user sends. The worst case is one user file evicted early and re-attached. The window is the 50 most recently uploaded files linked to the chat, and I'm happy with that trade-off rather than adding a timestamp to the link table.

re: Replace the architecture rewrite with TODO markers

That is the architecture.md change I've made and written, we don't want to commit todo markers to main.

re: Refresh chat files after tool-created attachments

The mechanics are right, but the impact is one tile: a chat at the cap, an attachment landing in the last few seconds of a run, and the oldest attachment in the timeline shows as available instead of expired until the next send, next run, or reload. Clicking it hits the same 404 fallbacks the UI already had. A stale file list can never flag a live file as expired, so there is no false positive side. Not worth an extra fetch per run end for that, so leaving it.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. What shall we delve into next?

Reviewed commit: 69d5aa0ef0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Long-running agent sessions hit the 50 attachment cap and then rejected
every further upload, screenshot, and attach_file call.

A chat now keeps its 50 most recent attachments. LinkChatFilesAfterLock
deletes the oldest chat_files rows on the chat in the same statement,
and the ON DELETE CASCADE on chat_file_links removes the links. Files in
the incoming batch, or still linked to another chat, are never evicted,
so the only remaining rejection is a single message that references
more than 50 files.

Evicted attachments render as "Attachment expired" in the UI, and
dispatch already replaces missing files with text placeholders.

Closes CODAGT-933
A single tool attachment can no longer be rejected at the cap: a one-file
batch always fits and eviction makes room. The mocked store returned a
rejection the real query cannot produce for this caller.
Text and download tiles now probe attachment availability on mount, so evicted files render the expired placeholder without a click. The expired tooltip names the attachment cap alongside the retention window.
The mount-time probe fetched every attachment blob to learn whether it still existed, and nothing revalidated it after eviction. The chat record already lists the files it holds and eviction is oldest-first, so the timeline now marks a referenced file as evicted only when it is absent from chat.files and referenced before the newest file that is still listed. References after that point are newer than the record and are presumed present until the next refetch.
@ethanndickson

Copy link
Copy Markdown
Member Author

@codex review

re: Include queued attachments in eviction ordering

The scan only loses its anchor when queued messages alone evict every historical file, which needs 50 or more files across queued messages while the chat is busy. A smaller queued batch on a full chat still flags the evicted files, because the newest surviving historical reference anchors the scan. Once the queued message is promoted its references enter the scan and the state corrects itself, and text attachments still fall back on click via the 404 path. Not worth the extra plumbing for that window.

re: Exclude links belonging only to deleted messages

This is pre-existing. On main those links count towards the cap and cause the attach to be rejected outright. Here they cause one earlier eviction and then age out as newer files arrive, so this branch is strictly better than main for that case. Unlinking on soft delete and queued delete is a reasonable follow-up but out of scope here.

re: Base eviction order on attachment time

Correct, the order is upload time rather than link time. The two differ only when a file is uploaded and linked later, which in the UI means a file sitting in the composer while the agent attaches 49 or more files before the user sends. The worst case is one user file evicted early and re-attached. The window is the 50 most recently uploaded files linked to the chat, and I'm happy with that trade-off rather than adding a timestamp to the link table.

re: Replace the architecture rewrite with TODO markers

That is the architecture.md change I've made and written, we don't want to commit todo markers to main.

re: Refresh chat files after tool-created attachments

The mechanics are right, but the impact is one tile: a chat at the cap, an attachment landing in the last few seconds of a run, and the oldest attachment in the timeline shows as available instead of expired until the next send, next run, or reload. Clicking it hits the same 404 fallbacks the UI already had. A stale file list can never flag a live file as expired, so there is no false positive side. Not worth an extra fetch per run end for that, so leaving it.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5715f8f642

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

… stories

Pixel screenshots the expired recording and download tiles. The fetch-count assertions on text and download tiles asserted nothing, since neither tile fetches on mount in any state.
@ethanndickson

Copy link
Copy Markdown
Member Author

@codex review

re: Include queued attachments in eviction ordering

The scan only loses its anchor when queued messages alone evict every historical file, which needs 50 or more files across queued messages while the chat is busy. A smaller queued batch on a full chat still flags the evicted files, because the newest surviving historical reference anchors the scan. Once the queued message is promoted its references enter the scan and the state corrects itself, and text attachments still fall back on click via the 404 path. Not worth the extra plumbing for that window.

re: Exclude links belonging only to deleted messages

This is pre-existing. On main those links count towards the cap and cause the attach to be rejected outright. Here they cause one earlier eviction and then age out as newer files arrive, so this branch is strictly better than main for that case. Unlinking on soft delete and queued delete is a reasonable follow-up but out of scope here.

re: Base eviction order on attachment time

Correct, the order is upload time rather than link time. The two differ only when a file is uploaded and linked later, which in the UI means a file sitting in the composer while the agent attaches 49 or more files before the user sends. The worst case is one user file evicted early and re-attached. The window is the 50 most recently uploaded files linked to the chat, and I'm happy with that trade-off rather than adding a timestamp to the link table.

re: Replace the architecture rewrite with TODO markers

That is the architecture.md change I've made and written, we don't want to commit todo markers to main.

re: Refresh chat files after tool-created attachments

The mechanics are right, but the impact is one tile: a chat at the cap, an attachment landing in the last few seconds of a run, and the oldest attachment in the timeline shows as available instead of expired until the next send, next run, or reload. Clicking it hits the same 404 fallbacks the UI already had. A stale file list can never flag a live file as expired, so there is no false positive side. Not worth an extra fetch per run end for that, so leaving it.

re: Move expired-recording checks out of Storybook play and Move downloadable-expiry checks into Vitest

Dropped both play functions since they only asserted the rendered state, which Pixel now captures. I have not moved the fetch-count check into Vitest: download tiles are plain links and text tiles fetch on click, so neither fetches on mount in any state and the assertion could never fail. The derivation that picks the expired tile is covered in messageHelpers.test.ts.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Delightful!

Reviewed commit: 6c5de89a3c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@ethanndickson
ethanndickson merged commit d8e3f4f into main Sep 10, 2026
51 of 53 checks passed
@ethanndickson
ethanndickson deleted the file-upload-5wrm branch September 10, 2026 03:40
@github-actions github-actions Bot locked and limited conversation to collaborators Sep 10, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants