Skip to content

workspace: Fix recent-projects cleanup wiping active workspaces#54224

Merged
maxbrunsfeld merged 3 commits intozed-industries:mainfrom
transitoryangel:fix/workspace
Apr 21, 2026
Merged

workspace: Fix recent-projects cleanup wiping active workspaces#54224
maxbrunsfeld merged 3 commits intozed-industries:mainfrom
transitoryangel:fix/workspace

Conversation

@transitoryangel
Copy link
Copy Markdown
Contributor

@transitoryangel transitoryangel commented Apr 18, 2026

The recent-projects picker, sidebar, welcome screen, and agent thread store all called recent_workspaces_on_disk, which combined listing with deleting stale rows. Its retention predicate rejected workspaces with no on-disk directory, including empty workspaces holding unsaved scratch buffers, and the resulting delete_workspace_by_id call cascaded into items, pane_groups, and the per-editor tables. For clarity, the method has been renamed to recent_workspaces_for_ui.

Meanwhile, last_session_workspace_locations used a slightly different form of the same predicate. The two disagreeing on what counts as a valid workspace caused #48799, Workspace WorkspaceId(N) not found errors on repeated launches (#50409), the test_window_edit_state_restoring_enabled flake (#50871), and the foreign key constraint fail on projects: open recent with a dirty scratch buffer (#51456).

Note that for the last issue mentioned (#51456) there is no save prompt for scratch buffers. This seems out of scope for this PR so I'll fix that after this is addressed.

Self-Review Checklist:

  • I've reviewed my own diff for quality, security, and reliability
  • Unsafe blocks (if any) have justifying comments
  • The content is consistent with the UI/UX checklist
  • Tests cover the new/changed behavior
  • Performance impact has been considered and is acceptable

Closes #48799
Closes #50409
Closes #50871
Closes #51456

Release Notes:

  • Fixed unsaved scratch buffers being lost across restarts and an occasional error when opening a recent project.
@cla-bot cla-bot Bot added the cla-signed The user has signed the Contributor License Agreement label Apr 18, 2026
@maxbrunsfeld maxbrunsfeld self-assigned this Apr 20, 2026
Copy link
Copy Markdown
Collaborator

@maxbrunsfeld maxbrunsfeld left a comment

Choose a reason for hiding this comment

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

Thanks for the PR @transitoryangel. This seems like a good direction, and I'm glad you figured out the source of those bugs. A couple of thoughts:

I don't think the GC logic is quite right. In particular, I think there's no need to have conditional behavior depending on whether a workspace has any items (currently workspace_has_items influences the is_stale check). As written, it seems like if a workspace does have items, we will preserve it in the database indefinitely. But I don't think there is any benefit to keeping a workspace in the DB if it has no paths, and it is not part of the last session (it would never be returned from any query).

I think a better fix for the bugs you identified would be to adjust the workspace GC so that it never deletes the workspace for the last session or the current session. When you reopen Zed, we should bring back all of your previously-opened workspaces, even if they have zero items.

I think that GC should explicitly happen after we have loaded the last session's workspaces, and it should delete any workspaces that have no valid paths (regardless of items).

Also, in terms of naming, I don't think think the name recent_workspaces_for_ui is quite right; the 'for ui' thing doesn't add a lot of specificity, since most database queries ultimately serve to populate some UI, directly or indirectly. I agree that _on_disk was a questionable suffix too. What do you think about the name recent_project_workspaces, to convey that it is tied to the "recent projects" feature?

@transitoryangel
Copy link
Copy Markdown
Contributor Author

Thanks for the feedback! The logic you suggest seems sound, and I'll work on changing things around.

Regarding the recent_workspaces_for_ui name - that was spotty in my opinion as well. recent_project_workspaces instead sounds pretty reasonable. I'll see how it fits in. Thank you for that suggestion!

@maxbrunsfeld maxbrunsfeld enabled auto-merge (squash) April 21, 2026 16:33
@maxbrunsfeld
Copy link
Copy Markdown
Collaborator

Great work @transitoryangel. Thank you!

@maxbrunsfeld maxbrunsfeld merged commit 1ea6eb4 into zed-industries:main Apr 21, 2026
31 checks passed
@JosephTLyons JosephTLyons added the guild Pull requests by someone in Zed Guild. NOTE: the label application is automated via github actions label Apr 21, 2026
eholk pushed a commit that referenced this pull request Apr 21, 2026
The recent-projects picker, sidebar, welcome screen, and agent thread
store all called `recent_workspaces_on_disk`, which combined listing
with deleting stale rows. Its retention predicate rejected workspaces
with no on-disk directory, including empty workspaces holding unsaved
scratch buffers, and the resulting `delete_workspace_by_id` call
cascaded into `items`, `pane_groups`, and the per-editor tables. For
clarity, the method has been renamed to `recent_workspaces_for_ui`.

Meanwhile, `last_session_workspace_locations` used a slightly different
form of the same predicate. The two disagreeing on what counts as a
valid workspace caused #48799, `Workspace WorkspaceId(N) not found`
errors on repeated launches (#50409), the
`test_window_edit_state_restoring_enabled` flake (#50871), and the
foreign key constraint fail on `projects: open recent` with a dirty
scratch buffer (#51456).

Note that for the last issue mentioned (#51456) there is no save prompt
for scratch buffers. This seems out of scope for this PR so I'll fix
that after this is addressed.

Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [ ] Unsafe blocks (if any) have justifying comments
- [ ] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Closes #48799
Closes #50409
Closes #50871
Closes #51456

Release Notes:

- Fixed unsaved scratch buffers being lost across restarts and an
occasional error when opening a recent project.

(cherry picked from commit 1ea6eb4)
eholk pushed a commit that referenced this pull request Apr 21, 2026
The recent-projects picker, sidebar, welcome screen, and agent thread
store all called `recent_workspaces_on_disk`, which combined listing
with deleting stale rows. Its retention predicate rejected workspaces
with no on-disk directory, including empty workspaces holding unsaved
scratch buffers, and the resulting `delete_workspace_by_id` call
cascaded into `items`, `pane_groups`, and the per-editor tables. For
clarity, the method has been renamed to `recent_workspaces_for_ui`.

Meanwhile, `last_session_workspace_locations` used a slightly different
form of the same predicate. The two disagreeing on what counts as a
valid workspace caused #48799, `Workspace WorkspaceId(N) not found`
errors on repeated launches (#50409), the
`test_window_edit_state_restoring_enabled` flake (#50871), and the
foreign key constraint fail on `projects: open recent` with a dirty
scratch buffer (#51456).

Note that for the last issue mentioned (#51456) there is no save prompt
for scratch buffers. This seems out of scope for this PR so I'll fix
that after this is addressed.

Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [ ] Unsafe blocks (if any) have justifying comments
- [ ] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Closes #48799
Closes #50409
Closes #50871
Closes #51456

Release Notes:

- Fixed unsaved scratch buffers being lost across restarts and an
occasional error when opening a recent project.

(cherry picked from commit 1ea6eb4)
eholk added a commit that referenced this pull request Apr 21, 2026
Cherry picks for the next v0.233.x preview release, focused on agent
panel and sidebar fixes and improvements. Built incrementally so CI can
validate as we go.

### Cherry-picked PRs

Listed in the order they were applied (chronological merge order on
`main`). `[x]` = applied on this branch.

- [x] #54055 — sidebar: Move to new workspace non-Wayland
- [x] #54417 — agent_ui: Sort thread import agents by display name
- [x] #54427 — agent_ui: Handle pagination of session/list correctly
when importing
- [x] #54402 — agent_ui: Remove history view
- [x] #54411 — agent_ui: Preserve session resume state after load errors
- [x] #54430 — Remove `AgentV2FeatureFlag`
- [x] #54360 — sidebar: Adjust display of workspaces in header's
ellipsis menu
- [x] #54224 — workspace: Fix recent-projects cleanup wiping active
workspaces
- [x] #54438 — Tweak wording around multi-folder project actions
- [x] #54450 — Close the empty project before adding a project to a
window

### Conflict resolution notes

- **#54402** — `crates/agent_ui/src/agent_panel.rs`: four hunks. The
incoming patch is against a
post-[#47154](#47154) (gpui
`Corner` → `Anchor` rename) tree, which is not on `v0.233.x`. Kept
`Corner` in the `gpui::{...}` import, dropped the now-unused
`DismissEvent` import, removed the `render_recent_entries_menu` function
body and both `.when(show_history_menu && !agent_v2_enabled, ...)` call
sites in the toolbar — so the end state matches what #54402 produces on
`main` modulo the `Corner`/`Anchor` spelling.
- **#54411** — Applied cleanly after #54402 (in the original failed
attempt, a test-helper hunk in `conversation_view.rs` collided with
history-view test code that #54402 removes; ordering #54402 first made
this a no-op).
- **#54430** — Applied cleanly after #54402 (same reason: depends on the
`has_history_for_selected_agent` / `agent_v2_enabled` locals being
gone).
- **#54224** — Applied cleanly, but the new
`recent_projects_picker_workspace` test helper in
`crates/workspace/src/persistence.rs` initializes a `bookmarks:
Default::default()` field on `SerializedWorkspace`. That field comes
from the Bookmarks MVP
([#51404](#51404) /
[#54174](#54174)) which is not
on `v0.233.x`, so we dropped that one line to match the struct shape on
this branch.

Release Notes:

- Fixed unsaved scratch buffers being lost across restarts and an
occasional error when opening a recent project.
([#54224](#54224))

---------

Co-authored-by: Cameron Mcloughlin <cameron.studdstreet@gmail.com>
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
Co-authored-by: Bennet Bo Fenner <bennet@zed.dev>
Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
Co-authored-by: Danilo Leal <67129314+danilo-leal@users.noreply.github.com>
Co-authored-by: allison <28279548+transitoryangel@users.noreply.github.com>
Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla-signed The user has signed the Contributor License Agreement guild Pull requests by someone in Zed Guild. NOTE: the label application is automated via github actions

4 participants