Fix Settings/Rules panes going stale after cross-window tab drag (APP-5311) - #14950
Draft
warp-agent-staging[bot] wants to merge 2 commits into
Draft
Fix Settings/Rules panes going stale after cross-window tab drag (APP-5311)#14950warp-agent-staging[bot] wants to merge 2 commits into
warp-agent-staging[bot] wants to merge 2 commits into
Conversation
…-5311) When a Settings (or AI-facts "Rules") tab is dragged into a new window, the transfer skips the normal PaneContent::detach/attach hooks (Workspace::prepare_for_transferred_tab_attach suppresses detach on the source window). This left three bugs: A. SettingsPaneManager/AIFactManager kept a stale locator on the source window pointing at a pane group that no longer lived there, so reopening Settings/Rules from that window silently no-op'd forever after the transferred tab was closed. B. A transferred SettingsView's SettingsViewEvent subscription remained bound to the workspace that originally created it, so actions taken from a Settings pane now hosted in window B (e.g. clicking "Rules") executed in the stale window A instead. C. The AIFactManager equivalent of A, compounded by B's routing bug. Fix: - PaneGroup::on_window_transferred now re-keys the SettingsPaneManager/ AIFactManager locator from the source window to the destination window, and re-homes the SettingsView/AIFactView event subscription from the source workspace to the destination workspace. - Workspace::open_settings_pane and open_ai_fact_collection_pane are now defensive: if the registered locator does not resolve to a live pane in the current window, they clear it and open a fresh tab/pane instead of silently doing nothing. Added regression tests in app/src/workspace/view_tests.rs covering all three symptoms via the same cross-window transfer primitives production code uses (transfer_view_tree_to_window + insert_transferred_tab_at_index + remove_tab_without_undo). Co-Authored-By: Warp Agent <agent@warp.dev>
Contributor
Author
There was a problem hiding this comment.
Overview
Fixes Settings/Rules panes going stale after a cross-window tab drag, covering all three reported symptoms. Two findings below need a human decision rather than a mechanical fix; the other review findings are already being addressed on this branch.
Concerns
- Singleton collision on transfer is unspecified behavior (
app/src/pane_group/mod.rs:8246,:8285). Dragging Settings or Rules into a destination window that already has that pane silently overwrites the destination manager locator while both panes stay live; when the transferred pane later closes, its detach clears the map and the original destination pane becomes untracked, so the next open creates a duplicate. The code's invariant is one Settings/Rules pane per window, but the intended collision behavior is a product decision: reject the transfer, focus the destination singleton and discard the source pane, or replace the existing destination pane. Please pick one and it will be enforced and tested for both pane types. - Visual proof is missing for a user-facing change. This changes desktop window behavior, but the only capture attached shows the login/onboarding blocker rather than the three flows working end to end. The agent environment's API key resolves to an agent identity that the backend rejects, so the app never got past sign-in; a user-account key is being arranged, after which a recording of all three flows will be attached here.
Verdict
Checks: build pass, tests pass, CI no failing checks (heavy jobs skipped while draft), visual proof missing
Found: 0 critical, 2 important, 0 suggestions, 0 nits (findings needing your judgment only)
…sion handling - PaneGroup::on_window_transferred no longer runs the Settings/AI-fact subscription rehoming synchronously; it's dispatched as a self-targeted deferred PaneGroupAction so a real drag (which runs while the source Workspace is already mid-update) doesn't panic with "Circular view update". - open_settings_pane/open_ai_fact_collection_pane now resolve the concrete SettingsPane/AIFactPane's own view via the locator instead of updating this window's native (possibly different) settings_pane/ai_fact_view, so page/search navigation and OpenSettings/OpenAIFactCollection routing target the pane that's actually hosting the request. - Narrowed SettingsPane::settings_view to pub(crate). - Enforced the one-Settings-pane/one-Rules-pane-per-window invariant on transfer: SettingsPaneManager/AIFactManager gained register_transferred_pane, which detects a collision with an existing pane instead of silently overwriting the destination locator. On collision, the transferred pane is discarded (its whole tab if it was the tab's only content, otherwise just the pane) and the pre-existing pane is kept and focused, via a second self-targeted deferred WorkspaceAction so the pane group being discarded is never touched while still mid-update. - Added regression tests: real single-tab-drag handoff path (proves no panic), page-navigation targeting the transferred pane, and collision reconciliation for both Settings and Rules panes. Co-Authored-By: Warp Agent <agent@warp.dev>
1 task
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.
Description
Fixes APP-5311: Settings (and the AI-facts "Rules" pane) could not be reopened after their tab was dragged into a new window and that window was closed.
Root cause. A cross-window tab drag intentionally suppresses the normal
PaneContent::detach/attachhooks on the source window (Workspace::prepare_for_transferred_tab_attach). This left three related bugs, all rooted in per-window singleton state (SettingsPaneManager,AIFactManager) and per-view event subscriptions not knowing the pane had moved:SettingsPaneManager/AIFactManagerkept a locator on the source window pointing at a pane group that no longer lived there. Reopening Settings/Rules from that window silently no-op'd forever, becausefocus_paneresolves the stale locator to nothing and returns early.SettingsView'sSettingsViewEventsubscription is registered once, inWorkspace::build_settings_views, against whichever workspace created it. It does not follow the view when the view transfers to another window. So an action taken from a Settings pane now hosted in window B (e.g. clicking "Rules") executed in the stale window A instead of window B.AIFactManagerequivalent of A, compounded by B's routing bug (clicking "Rules" from a transferred Settings pane routed to the wrong window'sopen_ai_fact_collection_pane, which then hit its own stale/absent locator and silently did nothing).Fix (initial):
PaneGroup::on_window_transferredre-keys theSettingsPaneManager/AIFactManagerlocator from the source window to the destination window whenever a transferred pane group contains aSettingsPane/AIFactPane, and re-homes theSettingsView/AIFactViewevent subscription from the source workspace to the destination workspace.Workspace::open_settings_paneandopen_ai_fact_collection_paneare defensive: if the registered locator does not resolve to a live pane in the current window, they clear it and open a fresh tab/pane instead of silently doing nothing.Fix (after adversarial review — this revision):
on_window_transferred, but in a real drag that runs while the sourceWorkspaceis already mid-update (handle_action(DropTab)→perform_handoff→CrossWindowTabDrag::execute_handoff_single_tab_to_other→AppContext::transfer_view_tree_to_window). CallingViewHandle::updateon that same, currently-removed-from-its-window view panicked with "Circular view update". It's now dispatched as a self-targeted, deferredPaneGroupAction(not run synchronously, and not routed through the ancestor chain, which isn't reliably populated for a just-transferred view until the next render pass).open_settings_pane/open_ai_fact_collection_panewere updating this window's own native (possibly unrendered)self.settings_pane/self.ai_fact_viewinstead of the concreteSettingsPane/AIFactPanethe locator actually points at. Page/search navigation andOpenAIFactCollectionrouting now resolve and update the pane that's actually hosting the transferred view.SettingsPane::settings_viewis nowpub(crate)instead ofpub.SettingsPaneManager/AIFactManagergainedregister_transferred_pane, which detects a collision with an existing pane in the destination window instead of silently overwriting the locator (which used to leave the pre-existing pane live but untracked). On collision, the transferred pane is discarded — closing its whole tab if it was the tab's only content, otherwise just the pane — and the pre-existing pane is kept and focused, via a second self-targeted deferredWorkspaceAction(DiscardDuplicateTransferredPane) so the discard never touches the transferred pane group while it's still mid-update.Linked Issue
Testing
Regression tests in
app/src/workspace/view_tests.rs, each exercising the production cross-window-transfer primitives (AppContext::transfer_view_tree_to_window/Workspace::perform_handoff+insert_transferred_tab_at_index+remove_tab_without_undo/remove_tab) rather than mocking them away:test_settings_pane_reopens_after_cross_window_transfer_and_close(symptom A)test_settings_pane_actions_execute_in_hosting_window_after_cross_window_transfer(symptom B)test_ai_fact_pane_reopens_after_cross_window_transfer_and_close(symptom C)test_settings_pane_transfer_via_real_handoff_path_does_not_panic— drives the real single-tabWorkspace::perform_handoffentry point (not a bare top-level transfer call) so the transfer runs nested inside the source workspace's own update, reproducing the call stack that used to panic.test_settings_pane_page_navigation_after_transfer_updates_transferred_view— proves page navigation updates the transferred pane's own view, not this window's unused native one.test_settings_pane_transfer_into_window_with_existing_pane_discards_duplicate/test_ai_fact_pane_transfer_into_window_with_existing_pane_discards_duplicate— collision reconciliation for both pane kinds: no duplicate survives, the pre-existing pane stays reachable, and closing it still lets a fresh one open.I verified the original three tests fail without the initial
on_window_transferredfix (by temporarily reverting just that change) and pass with it restored. The new tests were written against the reviewed (broken) intermediate states first and confirmed to fail there before the final fix made them pass.Ran:
cargo nextest run -p warpfor the touched modules (workspace::,pane_group::, plussettings/ai_fact/cross_windowfilters): 517 tests, all passing../script/format— clean.cargo clippy -p warp --all-targets -- -D warnings— clean.I have manually tested my changes locally with
./script/run(see note below)Manual/GUI verification note: I attempted to verify the fix end-to-end with a live build (
cargo build --bin warpsucceeded, then drove the running app via computer-use) but the sandbox'sWARP_API_KEYresolves to an agent identity rather than a real user account, so the app never leaves the logged-out onboarding screen (backend rejects it with "Expected a user account") and I could not reach the terminal UI to exercise the Settings tab. I'm reporting this limitation rather than claiming GUI verification that didn't happen — the recording/screenshot below document the auth blocker, not the fix in action. The fix is otherwise fully covered by the fail-before/pass-after regression tests above, including a dedicated test for the exact re-entrancy call stack that previously panicked.Screenshots / Videos
Computer-use video recordings
Warp Settings detach and reopen flow: Attempted run, blocked at onboarding due to the sandbox API key not being a user-account credential (see note above).
Computer-use screenshots
Warp stuck on the logged-out "Welcome to Warp" onboarding screen — the auth blocker described above, not the fixed flow.
Agent Mode
Conversation: https://staging.warp.dev/conversation/69399d8d-001f-4932-a315-3ccf9430e4b9
Run: https://oz.staging.warp.dev/runs/019ff1d9-0c2c-719e-92b5-86326ccefe3a
This PR was generated with Oz.