Repository navigation
Fix global search lifecycle after reopening - #9562
austinywang wants to merge 34 commits into
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a presentation controller for Global Search. It manages popover lifecycle, keyboard events, query execution, selection, dismissal, and cancellation. It also synchronizes panel titles and content with the live search index. Tests cover lifecycle, cancellation, refresh, capture races, deadlines, and title behavior. ChangesGlobal Search
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant MenubarSearchPopover
participant GlobalSearchPopoverPresentation
participant GlobalSearchCoordinator
participant GlobalSearchPanelCaptureManager
participant SearchIndex
MenubarSearchPopover->>GlobalSearchPopoverPresentation: begin presentation
GlobalSearchPopoverPresentation->>GlobalSearchCoordinator: refresh browse results
GlobalSearchCoordinator->>GlobalSearchPanelCaptureManager: refresh panel content
GlobalSearchPanelCaptureManager->>SearchIndex: upsert captured content
GlobalSearchPopoverPresentation->>SearchIndex: execute debounced query
SearchIndex-->>GlobalSearchPopoverPresentation: return search hits
GlobalSearchPopoverPresentation->>MenubarSearchPopover: publish results and selection
MenubarSearchPopover->>GlobalSearchPopoverPresentation: submit selected result
GlobalSearchPopoverPresentation->>GlobalSearchCoordinator: activate selected result
MenubarSearchPopover->>GlobalSearchPopoverPresentation: end presentation
Possibly related issues
Possibly related PRs
Suggested reviewers: Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (3 errors, 1 warning)
✅ Passed checks (21 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
162862d to
19c0cb6
Compare
Bugbot is paused — on-demand spend limit reachedBugbot uses usage-based billing for this team and has hit its on-demand spend limit. A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue. |
19c0cb6 to
b7c9340
Compare
Bugbot is paused — on-demand spend limit reachedBugbot uses usage-based billing for this team and has hit its on-demand spend limit. A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue. |
…rch-palette-enter-never-activa
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@cmuxTests/GlobalSearchShortcutBehaviorTests.swift`:
- Line 422: Remove the fixed-duration RunLoop delay on line 422 that calls
`RunLoop.main.run(until: Date.now.addingTimeInterval(0.05))`. Move the reopen
operation to execute immediately after `waitUntilGlobalSearchCloses()`
completes. If coordinator visibility is insufficient to verify the popover
close, add an explicit completion predicate wait instead of relying on a
fixed-duration delay to detect the close event.
In `@Sources/Search/MenubarSearchPopover.swift`:
- Around line 151-154: Update cancelSearchWork() to increment searchGeneration
before clearing searchDebounceTimer, invalidating already-queued debounce
handlers; have scheduleSearch() capture the incremented generation as its
current generation. Add a deterministic regression test covering cleanup racing
with queued scheduling, avoiding fixed delays and verifying stale work cannot
update results after cleanup.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 2da57111-0641-4c9b-a8ff-3b10721450b3
📒 Files selected for processing (4)
Sources/Search/GlobalSearchPopoverPresentation.swiftSources/Search/MenubarSearchPopover.swiftcmux.xcodeproj/project.pbxprojcmuxTests/GlobalSearchShortcutBehaviorTests.swift
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
Sources/Search/MenubarSearchPopover.swift (1)
38-54: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winGate
popoverDidCloseon the live presentation generation before tearing down.
dismiss()only schedulesperformanceClose;popoverDidCloselater callsendPresentation()and incrementspresentationGenerationunconditionally.refreshTask/scheduleSearchuse the generation thatbeginPresentation()set, whileremoveKeyMonitor()clears input handling from the displayed popover. Keep a copy of the presentation generation inpopoverDidCloseand only perform teardown when it matches the current presentation.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Sources/Search/MenubarSearchPopover.swift` around lines 38 - 54, Update popoverDidClose to capture the presentation generation associated with the closing popover and compare it with the current generation before calling presentation.endPresentation(), clearing dismissalHandler, or invoking the handler. Skip teardown for stale close notifications so the active presentation and its refreshTask/scheduleSearch generation remain intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@cmuxTests/GlobalSearchShortcutBehaviorTests.swift`:
- Around line 477-480: Update the test around GlobalSearchPopoverPresentation so
it waits for the scheduled search task to finish, not merely
debounce.waitUntilFinished(). Add an appropriate completion signal,
deadline-bounded predicate, or owned-task wait before asserting
presentation.results == browseResults, while preserving the existing
presentation closure and cancellation behavior.
In `@Sources/Search/GlobalSearchCoordinator.swift`:
- Around line 164-173: The title indexing flow in GlobalSearchCoordinator must
prevent a stale deletePanel mutation from completing after a newer upsert.
Serialize delete and upsert operations per panel, or track generations so
indexedTitleSnapshots[context.panelID] is committed only when that upsert is
still current; update the logic around cancelPanelPurge and index.upsert while
preserving cancellation behavior.
- Around line 168-173: Update the construction of GlobalSearchPanelContext so
its panelTitle is resolved through workspace.panelTitle(panelId:) rather than
panel.displayTitle, ensuring GlobalSearchTitleSnapshot compares workspace-owned
remote and custom titles and refreshes stale global-search documents.
In `@Sources/Search/GlobalSearchPopoverPresentation.swift`:
- Around line 130-133: Update the query-results handling near
GlobalSearchResultRow creation to reset selectedIndex to 0 whenever new results
replace results, matching reloadBrowseResults(). Remove the existing clamping
behavior so stale selection positions cannot carry over between queries.
---
Outside diff comments:
In `@Sources/Search/MenubarSearchPopover.swift`:
- Around line 38-54: Update popoverDidClose to capture the presentation
generation associated with the closing popover and compare it with the current
generation before calling presentation.endPresentation(), clearing
dismissalHandler, or invoking the handler. Skip teardown for stale close
notifications so the active presentation and its refreshTask/scheduleSearch
generation remain intact.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: b9133561-6901-4c02-9e8a-a1ffb6c38e03
📒 Files selected for processing (7)
Sources/Search/AppDelegate+GlobalSearch.swiftSources/Search/GlobalSearchCoordinator.swiftSources/Search/GlobalSearchPopoverPresentation.swiftSources/Search/GlobalSearchTitleSnapshot.swiftSources/Search/MenubarSearchPopover.swiftcmux.xcodeproj/project.pbxprojcmuxTests/GlobalSearchShortcutBehaviorTests.swift
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@cmuxTests/GlobalSearchShortcutBehaviorTests.swift`:
- Around line 750-763: Update the private waitUntil polling helper to use the
project’s injected test clock or monotonic deadline helper instead of Date.now
for both deadline creation and expiration checks. Preserve the existing
predicate polling and timeout behavior while avoiding wall-clock APIs in this
test.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 74afc497-54fe-455e-bfad-2c5e1594e232
📒 Files selected for processing (7)
Sources/Search/AppDelegate+GlobalSearch.swiftSources/Search/GlobalSearchCoordinator.swiftSources/Search/GlobalSearchPanelCaptureManager.swiftSources/Search/GlobalSearchPopoverPresentation.swiftSources/Search/MenubarSearchPopover.swiftSources/Search/SearchIndex.swiftcmuxTests/GlobalSearchShortcutBehaviorTests.swift
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
cmuxTests/GlobalSearchShortcutBehaviorTests.swift (3)
937-948: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winComplete popover dismissal during cleanup.
Line 938 starts dismissal, then the helper immediately resets shortcut routing and closes the main window. At Line 391, Line 424, and Line 669, the tests do not treat the same dismissal as synchronous. If a test exits with the palette open, pending delegate work can affect the next test through
GlobalSearchCoordinator.shared. Wait forwaitUntilGlobalSearchCloses()before resetting shared routing state and closing the window.As per coding guidelines, “Isolate shared static, global,
UserDefaults, file, and related state per test, resetting it insetUpandtearDownas appropriate.”🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@cmuxTests/GlobalSearchShortcutBehaviorTests.swift` around lines 937 - 948, The closeWindow method begins dismissing the palette with GlobalSearchCoordinator.shared.dismissPalette() but proceeds immediately to reset state and close the window without waiting for that dismissal to complete. This causes the palette to remain open across test boundaries, polluting the shared GlobalSearchCoordinator state. Add a call to waitUntilGlobalSearchCloses() immediately after the dismissPalette call to ensure the popover fully closes before the DEBUG block executes the debugResetShortcutRoutingStateForTesting and subsequent window closing logic.Source: Coding guidelines
795-808: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winUse a monotonic deadline in
waitUntil.Line 800 derives the timeout from
Date.now. A system-clock change can make the predicate poll expire early or run longer. Use the project test clock or a monotonic deadline helper.As per coding guidelines, “Test code must avoid real wall-clock dependencies: use injected virtual clocks and advance them manually for time-driven behavior.”
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@cmuxTests/GlobalSearchShortcutBehaviorTests.swift` around lines 795 - 808, Update the waitUntil function to use a monotonic deadline instead of wall-clock time. Replace the deadline initialization that currently uses Date.now.addingTimeInterval(timeout) with a monotonic deadline approach such as the project's test clock or a monotonic deadline helper. Update the while condition that checks the deadline to use the same monotonic time source, ensuring the timeout logic is immune to system clock changes.Source: Coding guidelines
440-443: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winAssert activation of the selected result.
Line 440 only proves that Return closes the palette. A handler that closes the palette without activating the selected browse result also passes. Capture the expected browse target before reopening, then assert its focused panel, selected workspace, or activation callback after the key event.
As per coding guidelines, “When a user reports that tests missed a bug, add behavior-level coverage for the exact reproduction path before claiming the fix is complete.”
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@cmuxTests/GlobalSearchShortcutBehaviorTests.swift` around lines 440 - 443, The current test only validates that pressing Return closes the global search palette via waitUntilGlobalSearchCloses, but does not verify that the selected result was actually activated. Capture the expected browse target before the popover reopens and the key event is triggered. After the waitUntilGlobalSearchCloses call succeeds, add assertions to verify the target was activated by checking its focused panel property, selected workspace state, or activation callback to ensure the selected result was properly activated and not just closed without selection.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@cmuxTests/GlobalSearchShortcutBehaviorTests.swift`:
- Around line 731-738: Update the initial-refresh test to exercise
GlobalSearchCoordinator.refreshLiveIndex() and await its completion before
asserting searchability, or otherwise explicitly await the capture completion
produced by captureManager.refreshPanelContent(for:). Ensure the assertion that
the newly discovered Markdown panel is searchable runs only after
coordinator-completed Markdown capture work.
---
Outside diff comments:
In `@cmuxTests/GlobalSearchShortcutBehaviorTests.swift`:
- Around line 937-948: The closeWindow method begins dismissing the palette with
GlobalSearchCoordinator.shared.dismissPalette() but proceeds immediately to
reset state and close the window without waiting for that dismissal to complete.
This causes the palette to remain open across test boundaries, polluting the
shared GlobalSearchCoordinator state. Add a call to
waitUntilGlobalSearchCloses() immediately after the dismissPalette call to
ensure the popover fully closes before the DEBUG block executes the
debugResetShortcutRoutingStateForTesting and subsequent window closing logic.
- Around line 795-808: Update the waitUntil function to use a monotonic deadline
instead of wall-clock time. Replace the deadline initialization that currently
uses Date.now.addingTimeInterval(timeout) with a monotonic deadline approach
such as the project's test clock or a monotonic deadline helper. Update the
while condition that checks the deadline to use the same monotonic time source,
ensuring the timeout logic is immune to system clock changes.
- Around line 440-443: The current test only validates that pressing Return
closes the global search palette via waitUntilGlobalSearchCloses, but does not
verify that the selected result was actually activated. Capture the expected
browse target before the popover reopens and the key event is triggered. After
the waitUntilGlobalSearchCloses call succeeds, add assertions to verify the
target was activated by checking its focused panel property, selected workspace
state, or activation callback to ensure the selected result was properly
activated and not just closed without selection.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: ca7c1acb-9926-43bd-a7f5-dbab3701d587
📒 Files selected for processing (1)
cmuxTests/GlobalSearchShortcutBehaviorTests.swift
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
Sources/Search/GlobalSearchCoordinator.swift (1)
118-151: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy liftA purge already awaiting
deletePanelcan still erase a newer title upsert.
cancelPanelPurge(Line 153) only cancels the task and clearspanelPurgeTaskIDs. A purge task that has already passed the guard at Line 141 and is suspended insidetry await index.deletePanel(panelID)continues to completion.refreshLivePanelTitlescallscancelPanelPurgeat Line 175 and then upserts the title at Line 181, so this ordering is possible:
purgePanel(id:)schedules the delete and reachesawait index.deletePanel(panelID).- The panel becomes live again;
cancelPanelPurgeclears the bookkeeping.refreshLivePanelTitlesupserts the title document.- The suspended
deletePanelcompletes and removes every document for that panel, including the new title document.- Line 186 commits
indexedTitleSnapshots[context.panelID] = snapshot, so later refreshes skip the upsert and the panel stays missing from search results until its title changes.
titleIndexGenerationdoes not cover this case, because a purge does not change the generation. Serialize per-panel index mutations, or record a purge revision that the title upsert must re-check before committing the snapshot.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Sources/Search/GlobalSearchCoordinator.swift` around lines 118 - 151, Prevent an in-flight purge from deleting a title re-upserted by refreshLivePanelTitles. Update purgePanel and the related cancelPanelPurge/refreshLivePanelTitles flow to serialize per-panel index mutations or track and revalidate a purge revision before committing indexedTitleSnapshots, ensuring a completed delete cannot overwrite a newer upsert or leave its snapshot marked as indexed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@Sources/Search/GlobalSearchCoordinator.swift`:
- Around line 96-99: The loop calling await
captureManager.refreshPanelContent(for: context) blocks the palette open because
refreshPanelContent awaits Markdown panel initialization. Wrap the
refreshPanelContent calls in a non-awaited background Task so the palette can
open without waiting for panel content to load, allowing the index build to
proceed independently after presentation.
---
Outside diff comments:
In `@Sources/Search/GlobalSearchCoordinator.swift`:
- Around line 118-151: Prevent an in-flight purge from deleting a title
re-upserted by refreshLivePanelTitles. Update purgePanel and the related
cancelPanelPurge/refreshLivePanelTitles flow to serialize per-panel index
mutations or track and revalidate a purge revision before committing
indexedTitleSnapshots, ensuring a completed delete cannot overwrite a newer
upsert or leave its snapshot marked as indexed.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 65d947c2-a326-457c-8263-f7f3c9ecc986
📒 Files selected for processing (2)
Sources/Search/GlobalSearchCoordinator.swiftSources/Search/GlobalSearchPanelCaptureManager.swift
|
Follow-up on the CodeRabbit outside-diff findings:
|
fda7d37 to
4f616a1
Compare
Bugbot is paused — on-demand spend limit reachedBugbot uses usage-based billing for this team and has hit its on-demand spend limit. A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue. |
bb8801a to
3b6e714
Compare
Bugbot is paused — on-demand spend limit reachedBugbot uses usage-based billing for this team and has hit its on-demand spend limit. A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue. |
Bugbot is paused — on-demand spend limit reachedBugbot uses usage-based billing for this team and has hit its on-demand spend limit. A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue. |
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@cmuxTests/GlobalSearchShortcutBehaviorTests.swift`:
- Around line 1122-1128: Remove the negative fixed-duration wait that polls
refreshFinishedCount after thirdIndexRequestStarted.wait() in the relevant test.
Rely on thirdIndexRequestStarted as the deterministic proof that the latest
capture was followed, and retain the existing positive completion assertion that
follows.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 78fe26df-7dd9-44fa-a295-67d442331e41
📒 Files selected for processing (8)
Sources/Search/GlobalSearchCoordinator.swiftSources/Search/GlobalSearchPanelCaptureCompletion.swiftSources/Search/GlobalSearchPanelCaptureDeadline.swiftSources/Search/GlobalSearchPanelCaptureManager.swiftSources/Search/GlobalSearchPopoverPresentation.swiftSources/Search/GlobalSearchPopoverSelectionUpdate.swiftcmux.xcodeproj/project.pbxprojcmuxTests/GlobalSearchShortcutBehaviorTests.swift
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
Follow-up on the automated pre-merge prompts:
The final branch-wide canonical review and cmux policy check both report no actionable findings. |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
cmuxTests/GlobalSearchShortcutBehaviorTests.swift (1)
1029-1041: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winMake the replacement capture observable.
Both the initial refresh and
captureBrowserPanel(browserPanel)are validated with the sametoken. An implementation that commits only the first capture can still pass the final search assertion. Change the browser content between captures, or use generation-specific markers, and assert that the replacement marker is indexed.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@cmuxTests/GlobalSearchShortcutBehaviorTests.swift` around lines 1029 - 1041, Update the test around refreshTask and captureManager.captureBrowserPanel(browserPanel) so the replacement capture produces distinct observable content from the initial capture, using changed browser content or generation-specific markers. Assert that index.search(token) contains the replacement marker for the browser panel, ensuring the superseding capture—not merely the initial capture—is committed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@cmuxTests/GlobalSearchShortcutBehaviorTests.swift`:
- Around line 1029-1041: Update the test around refreshTask and
captureManager.captureBrowserPanel(browserPanel) so the replacement capture
produces distinct observable content from the initial capture, using changed
browser content or generation-specific markers. Assert that index.search(token)
contains the replacement marker for the browser panel, ensuring the superseding
capture—not merely the initial capture—is committed.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 8f09e414-6f9d-40f6-9140-e5ecf9ee855d
📒 Files selected for processing (1)
cmuxTests/GlobalSearchShortcutBehaviorTests.swift
Bugbot is paused — on-demand spend limit reachedBugbot uses usage-based billing for this team and has hit its on-demand spend limit. A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue. |
Fixes #7445
Summary
The global-search popover retains its SwiftUI content view, so SwiftUI appearance callbacks were not a reliable per-presentation lifecycle. The popover delegate now drives a dedicated presentation owner that clears retained query/results, reinstalls Return and navigation routing, cancels stale work, restores focus, and refreshes the live index on every open.
Live search indexing is incremental and cancellation-safe: title updates are deduplicated and resolved in one workspace pass, panel captures follow replacements within one bounded presentation deadline, closed panels are reconciled, cancelled SQLite reads terminate through the progress handler, and discarded browser refreshes preserve the last authoritative indexed content.
Testing
3c2e7b7fa2:reopeningSearchClearsRetainedQuery()andreopeningSearchRefreshesLiveIndex()failed against the old behavior.b7c9340290: the same tests passed, together with Return activation after reopen.894e992b0c:discardedBrowserRefreshPreservesPreviouslyIndexedContent()failed against the old behavior.b74f340d38: the regression passed.eca3285f50on the leased Xcode 26.3 fleet host:cmux-unitbuild-for-testing: succeeded with isolated DerivedData.GlobalSearchShortcutBehaviorTestspassed, covering the reopen lifecycle, Return activation, batched local/remote title resolution, cancelled index operations and active SQLite progress cancellation, deterministic Markdown/browser replacement ordering, and presentation-owned/late/reused capture invalidation../scripts/lint-pbxproj-test-wiring.sh: passed../scripts/reload-cloud.sh --tag sym7445 --launch(run 30948727541,blacksmith-6vcpu-macos-26,BUILD_OK)./tmp/cmux-debug-sym7445.sock:zz7445eca8041404; Return changed the active workspace from38C34E38-3263-4D41-B9B9-2810349F4BC2toF6C9853B-19D3-4EF2-85FB-3DE1E3BD59F1.zz7445eca8041405only after that first palette session, reopened Search without clearing the field, typed only the new title, and Return changed the active workspace fromF6C9853B-19D3-4EF2-85FB-3DE1E3BD59F1to5E4A0760-7E89-43E6-B062-87368C3DC9C6.pkill -f "DerivedData/cmux-sym7445"; confirmed no tagged process remained and removed the stale tagged socket.