Fix agent threads missing from sidebar after upgrade (#54723) (cherry-pick to stable)#54752
Merged
zed-zippy[bot] merged 1 commit intov0.233.xfrom Apr 24, 2026
Merged
Conversation
Fixes #54618. ## Summary After upgrading from `v0.232.x` to `v0.233.5+`, many users reported that most of their agent threads "disappeared" from the sidebar. The threads are still on disk in the legacy `threads.db` — but they never make it into `sidebar_threads`, which is the only table the new sidebar UI reads from, so they're unreachable from the UI. The root cause is a race between `ThreadStore::reload` and `migrate_thread_metadata`: - `ThreadStore::new` constructs with an empty in-memory `Vec` and kicks off `reload()` as a fire-and-forget task. - `migrate_thread_metadata` runs during `agent_ui::init` and reads `ThreadStore::global(cx).read(cx).entries()` synchronously, without awaiting that reload. - On cold boot the migration observes an empty iterator, early-returns on `to_migrate.is_empty()`, and never populates `sidebar_threads`. The migration runs every launch, so it keeps losing the race forever. Empirically, the only rows that *did* end up in `sidebar_threads` for affected users came from `handle_conversation_event` writing rows when the user actively interacted with a thread. That's why users would typically see a handful of recently-touched threads rather than their full history. ## Fix 1. `ThreadStore` now tracks its current reload as a `Shared<Task<()>>` and exposes it via `reload_task()`. `migrate_thread_metadata` awaits this before reading `entries()`. 2. Move the top-5-per-project unarchive pass out from under the `is_first_migration` guard. Previously the rescue only ran when `sidebar_threads` was empty, which meant any user who had even one pre-existing row (e.g. from interacting with a thread on a prior launch) got every subsequent batch of migrated threads archived with no rescue. Running the rescue per-batch is stateless and idempotent — a user who bounces between older releases and newer ones still gets their top 5 per project surfaced each time a new batch is migrated. No new KVP flag or one-shot backfill is needed: because the migration already dedupes by session_id and runs on every launch, the next cold boot on a fixed build picks up any legacy threads that earlier launches missed. ## Structure The PR is split into two commits to make the diff easy to verify: 1. **Regression test** — fails on `main`, reproduces the cold-boot race by seeding the legacy DB, reinitializing `ThreadStore` to get a fresh empty cache, and running the migration without parking first. 2. **The fix** — gates entries reads on `reload_task().await`, and adjusts the per-batch rescue policy. Updates one existing test's assertions to match the new per-batch rescue policy. ## Validation - The regression test fails on `main`, passes after the fix. - All `agent_ui`, `agent::thread_store`, and `sidebar` lib tests pass. - `./script/clippy -p agent -p agent_ui` is clean. - End-to-end repro with a real on-disk data dir: launched `v0.230.2 → v0.231.2 → v0.232.3 → v0.233.8` as a non-staff user, creating 15 threads and interacting with one, reproduced the reported state (1 thread in sidebar, 14 missing). Launched a debug build of this branch against the same data dir: `Migrating 14 thread store entries` in the log, 15 rows in `sidebar_threads` with 6 unarchived (the interacted-with thread plus the 5 most recent), 9 archived and reachable from the archive view. Release Notes: - Fixed agent threads appearing to be missing after upgrading to the parallel-agents sidebar. The thread-metadata migration was racing against the on-disk thread store's async reload and skipping itself on every launch. Upgrading to this build runs the migration successfully; previously-missing threads are surfaced either in the sidebar (5 most recent per project) or in the archive view.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Cherry-pick of #54723 to stable
Fixes #54618.
Summary
After upgrading from
v0.232.xtov0.233.5+, many users reported thatmost
of their agent threads "disappeared" from the sidebar. The threads are
still
on disk in the legacy
threads.db— but they never make it intosidebar_threads, which is the only table the new sidebar UI readsfrom, so
they're unreachable from the UI.
The root cause is a race between
ThreadStore::reloadandmigrate_thread_metadata:ThreadStore::newconstructs with an empty in-memoryVecand kicksoff
reload()as a fire-and-forget task.migrate_thread_metadataruns duringagent_ui::initand readsThreadStore::global(cx).read(cx).entries()synchronously, withoutawaiting that reload.
on
to_migrate.is_empty(), and never populatessidebar_threads. Themigration runs every launch, so it keeps losing the race forever.
Empirically, the only rows that did end up in
sidebar_threadsforaffected users came from
handle_conversation_eventwriting rows whenthe
user actively interacted with a thread. That's why users would typically
see
a handful of recently-touched threads rather than their full history.
Fix
ThreadStorenow tracks its current reload as aShared<Task<()>>and
exposes it via
reload_task().migrate_thread_metadataawaits thisbefore reading
entries().is_first_migrationguard. Previously the rescue only ran whensidebar_threadswas empty, which meant any user who had even onepre-existing row (e.g. from interacting with a thread on a prior launch)
got every subsequent batch of migrated threads archived with no rescue.
Running the rescue per-batch is stateless and idempotent — a user who
bounces between older releases and newer ones still gets their top 5 per
project surfaced each time a new batch is migrated.
No new KVP flag or one-shot backfill is needed: because the migration
already dedupes by session_id and runs on every launch, the next cold
boot
on a fixed build picks up any legacy threads that earlier launches
missed.
Structure
The PR is split into two commits to make the diff easy to verify:
main, reproduces the cold-boot raceby
seeding the legacy DB, reinitializing
ThreadStoreto get a fresh emptycache, and running the migration without parking first.
reload_task().await, andadjusts
the per-batch rescue policy. Updates one existing test's assertions to
match the new per-batch rescue policy.
Validation
main, passes after the fix.agent_ui,agent::thread_store, andsidebarlib tests pass../script/clippy -p agent -p agent_uiis clean.v0.230.2 → v0.231.2 → v0.232.3 → v0.233.8as a non-staff user, creating 15 threadsand interacting with one, reproduced the reported state (1 thread in
sidebar, 14 missing). Launched a debug build of this branch against the
same data dir:
Migrating 14 thread store entriesin the log, 15 rowsin
sidebar_threadswith 6 unarchived (the interacted-with thread plus the5 most recent), 9 archived and reachable from the archive view.
Release Notes:
parallel-agents sidebar. The thread-metadata migration was racing
against the on-disk thread store's async reload and skipping itself on
every launch. Upgrading to this build runs the migration successfully;
previously-missing threads are surfaced either in the sidebar (5 most
recent per project) or in the archive view.