Hold thread folds on the host so every client shows the same list - #496
Merged
Merged
Conversation
The thread list's parent folds (which threads' child rows are hidden) were client-local: SessionsSidebar seeded them once at construction from the index it had at that moment and toggled them in memory. A phone attaches before its index arrives, so it seeded an empty set and every parent with agents opened expanded on each launch, and no two clients ever agreed on the list. The fold set now lives in Settings as collapsed_threads, replicated on the settings topic like collapsed_projects. Clients change it through the new SetThreadCollapsed command (rejected for an unknown thread), a deleted thread leaves no fold behind, and host start folds every thread that already has a visible child, which is the startup rule the client used to apply. The sidebar reads folds from the settings replica and, whenever a thread is selected on any client, asks the host to unfold its ancestors, which also restores the previously client-side exception for the restored selection. The one-for-one clone of the seeding rule and the local fold set are gone from the sidebar. Tests: host_start_folds_only_parents_with_visible_children and thread_fold_is_host_state_shared_over_the_pipe_and_pruned_with_the_thread (runtime); host_folds_hide_child_rows_and_selecting_a_child_unfolds_on_the_host and the reworked disclosure part of compact_families_keep_activity_order_indent_and_collapse (ui) drive the fold through the protocol instead of the removed local set.
Tryanks
force-pushed
the
sync-thread-folds
branch
from
September 21, 2026 07:31
8d58574 to
836c459
Compare
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.
Behaviour
Which parent threads have their child rows folded in the thread list is now host state shared by every client, like the project-group folds already were. A phone attached to a desktop host sees exactly the desktop's folds, and folding or expanding on either side shows on the other. Previously the fold set was client-local and seeded once at sidebar construction from whatever index the client had; a phone attaches before its index arrives, so it seeded nothing and every thread with agents opened expanded on each launch.
The startup rule is unchanged, just moved: host start folds every thread that already has a visible child. Selecting a thread on any client asks the host to unfold its ancestors, so the restored selection is on screen as before (and now also when a hidden child is reached through search).
Changes
core:Settings.collapsed_threads(absent in older files, skipped when empty; kept across a settings reset with the other sidebar state).protocol:Command::SetThreadCollapsed { session_id, collapsed }. Additive, as with earlier command additions.runtime:AppState::set_thread_collapsedpersists and replicates; the command is rejected withunknown_sessionfor an unknown thread;delete_sessionprunes the fold;startup_collapsed_threadsseeds at host start.ui:WorkspaceStoreexposes the replica (collapsed_threads,is_thread_collapsed) and the intent;SessionsSidebarloses its local fold set and the duplicate seeding rule, and every fold read or toggle goes through the store."Show more" expansion past the collapsed limit and the settled-section toggles stay client-local; they are view state, not list content.
Tests
host_start_folds_only_parents_with_visible_children(leaves, parents of only archived children, and parent ids no session carries stay unfolded),thread_fold_is_host_state_shared_over_the_pipe_and_pruned_with_the_thread(ack,unknown_session, settings replication, prune on delete);reset_settings_...also asserts the new field survives.host_folds_hide_child_rows_and_selecting_a_child_unfolds_on_the_host(a fold from the settings topic hides the child; selecting it sendsSetThreadCollapsed(parent, false)instead of unfolding locally); the disclosure part ofcompact_families_keep_activity_order_indent_and_collapsenow asserts the request and drives the rows from the host's settings. The removedstartup_folds_only_existing_visible_parents_outside_the_active_chainunit test tested the client-side seeding function; its rule is covered by the runtime test and the active-chain exception by the new ui test. The tests that set the local set directly now send it on the settings topic.Checks run
cargo fmt --all --checkcargo clippy --workspace --all-targets --locked -- -D warningscargo nextest run --workspace --locked(904 passed, 6 skipped)Not run locally: iOS/Android/Web builds (CI). Not exercised on a device.