Repository navigation
Darling Viewer: the Recommendations and FinOps pickers keep their servers when the server list reloads - #4947
Merged
Conversation
…ith the seam they drive PickerSelectionAfterReload holds what a rebuild does today: every picker ends on the sidebar's server. The source tests pin the shell wiring. Both fail here and pass with the fix.
…ndations and FinOps pickers on their servers A rebuild restored the sidebar selection without the suppression guard, so its handler moved both pickers to the sidebar's server, and the reload had already reset the Recommendations picker to the first server. The restore now runs under the guard and starts the visible tab's load itself. Each picker keeps its server while it exists. A picker whose server is gone takes the sidebar's server, as the initial load does.
There was a problem hiding this comment.
Verdict: LGTM
Claims
- VERIFIED — pre-fix
LoadServersAsyncset the Recommendations picker withRecommendationsServerSelector.SelectedIndex = 0— confirmed in the diff's removed lines (oldMainWindow.xaml.cs), replaced byPickerSelectionAfterReload. - VERIFIED — the old sidebar restore ran unguarded and triggered
ServerList_SelectionChanged→SyncAggregateServerSelectors, moving both pickers — confirmed by readingServerList_SelectionChanged(MainWindow.xaml.cs:860-880) andSyncAggregateServerSelectors(MainWindow.xaml.cs:890-904), and the diff removing the unguardedServerList.SelectedItem = _fleet.ResolveSelection(...)line. - VERIFIED —
FinOpsTab.SetServersalready matched by id before this PR — confirmed in the diff's old body (servers.FirstOrDefault(s => s.ServerId == pid)). - VERIFIED —
ReapplyFavoritesToServerListrestored the sidebar the same unguarded way — confirmed in MainWindow.ServerManagement.cs diff, now routed throughRestoreSidebarSelection. - VERIFIED —
PickerSelectionAfterReload's keep → sidebar → first-server fallback chain — ViewerServerSetSync.cs:117-119, exercised by 4 real (non-source-pin) unit tests inViewerServerSetSyncTests.cs. - VERIFIED — Recommendations and
FinOpsTab.SetServersboth call the new seam — MainWindow.xaml.cs:1081, FinOpsTab.xaml.cs:85. - VERIFIED — the FinOps tab's re-read on tab activation now also passes the sidebar's server — MainWindow.xaml.cs:1219-1221 (
LoadVisibleTabAsync). - VERIFIED —
RestoreSidebarSelectionsets the sidebar under_suppressSidebarSelectionand starts oneRefreshVisibleAsync— MainWindow.xaml.cs:945-961; both the reload and the favorites re-sort call it (MainWindow.xaml.cs:1100, MainWindow.ServerManagement.cs:86). - VERIFIED — the initial load passes no previous picker server, so both pickers start on the sidebar's server —
preserveSelection ? previousRecoServer : nullandkeepSelection: preserveSelectionat MainWindow.xaml.cs:1082,1089. - VERIFIED — a sidebar click still moves both pickers —
ServerList_SelectionChangedis unchanged and still callsSyncAggregateServerSelectorsfor the non-suppressed path. - VERIFIED — Lite parity: Lite's FinOps/Recommendations tabs already restore selection by id in their own
RefreshServerList(Lite/Controls/FinOpsTab.xaml.cs:101-137, RecommendationsTab.xaml.cs's analogous method), and Lite's sidebar has noSyncAggregateServerSelectors-equivalent that drives those pickers — confirmed by grep acrossLite/MainWindow.xaml.csand the two controls. No Lite change needed. - UNVERIFIED — the specific RED/GREEN test results at commits
3cce5dd88/23ea3da59, and the full-suite count (19877 total, 0 failed) — these commit SHAs aren't present in this shallow checkout and no test run can be executed from a read-only tree. - UNVERIFIED — the manual UIA walkthrough (sidebar/picker behavior across add/remove/favorite on a live 3-server store) — requires a running Viewer and a live SQL Server store, not checkable here.
Riskiest lines
Darling/PerformanceMonitor.Darling.Viewer/ViewerServerSetSync.cs:117-119— the keep→sidebar→first fallback chain both pickers now share — pinned byPicker_KeepsItsServer_WhileItExists_WhereverTheSidebarIs,Picker_WhoseServerWasRemoved_FollowsTheSidebar_AsOnAFreshLoad,FreshLoad_StartsThePicker_OnTheSidebarsServer,NothingToKeepOrFollow_TakesTheFirstServer_AndAnEmptyListSelectsNothing(real unit tests against the static method, not source pins).Darling/PerformanceMonitor.Darling.Viewer/MainWindow.xaml.cs:945-961(RestoreSidebarSelection) — if the_suppressSidebarSelectionguard ever wrapped less than theSelectedItemassignment, every reload/favorites re-sort would silently reintroduce the exact bug this PR fixes (both pickers hijacked back to the sidebar server) — pinned byReload_AndTheFavoritesResort_RestoreTheSidebar_WithoutMovingThePickers, but that test (like the whole Darling.Tests WPF surface, perViewerFinOpsPickerRefreshPinTests.cs:17— "WPF cannot run here") is a source-text pin, not an executed control; true runtime behavior rests on the PR's manual UIA check.Darling/PerformanceMonitor.Darling.Viewer/FinOpsTab.xaml.cs:93— the filter/drill-reset condition (now.ServerId != previousId) that must fire on a real switch and stay silent on a kept selection (#2306 regression surface) — pinned byFinOpsPicker_KeepsItsServer_AndFallsBackToTheSidebarsServer_AtBothOfItsRebuildsandViewerFinOpsPickerRefreshPinTests, same source-text-pin caveat as above.
Traps near this diff
- "Lite and Darling ship in parity (CONTRIBUTING's two-store rule): a fix to one SKU's reader, tool, alert or description without the counterpart's is a finding, not a nit" — does not apply. Lite's
FinOpsTab.xaml.cs:101-137andRecommendationsTab.xaml.csalready restore their own picker's server by id inside their ownRefreshServerList, and Lite's sidebar (Lite/MainWindow.xaml.cs) has no code path analogous toSyncAggregateServerSelectorsthat reaches into those pickers — there is no Lite bug for this PR to fix in parallel. - No SQL, collector/delta, analysis/scorer, alerting, storage/MCP-payload, or CI/workflow trap lines are near this diff (pure WPF selection-state plumbing in the Darling Viewer) — none apply.
Divergence (blind read vs PR body)
None — the body's account of the cause, the fix, and the "every list that reload rebuilds" survey all match what the diff and surrounding code show.
Reviewer checklist
- Darling.Tests has no WPF execution (confirmed by
ViewerFinOpsPickerRefreshPinTests.cs:17's own comment); the real behavior change here is only verified by the manual UIA walkthrough in the PR body — worth a human re-running those same 4 steps (sidebar on A, both pickers on B, remove/re-add/remove-B outside the Viewer) against a live build before release.
…and every sidebar selection write runs under the guard
…'s servers after its await, so a change made during the load is kept
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.
What changes
In the Darling Viewer, the Recommendations and FinOps tabs each have a server picker. When the server list reloaded, both pickers moved to the sidebar's server, and the user's choice was lost. The list reloads after a local add, edit or remove, and after the server-list sync sees a server added or removed outside this window (#4928). Pinning or unpinning a favorite had the same effect.
Now each picker keeps its server while that server exists. A picker whose server was removed takes the sidebar's server, as on the initial load.
Cause
LoadServersAsync(MainWindow.xaml.cs) set the Recommendations picker to the first server (SelectedIndex = 0).ServerList_SelectionChanged, and its call toSyncAggregateServerSelectorsmoved both pickers to the sidebar's server.FinOpsTab.SetServersalready kept its own server by id, but the sidebar restore then moved it.ReapplyFavoritesToServerList, the favorites re-sort, restored the sidebar in the same way. Pinning and unpinning both run it.LoadServersAsyncread the sidebar's and the Recommendations picker's servers before it awaited the server list. So a change made while the list loaded was put back.Fix
ViewerServerSetSync.PickerSelectionAfterReloadpicks the server that a picker shows after a rebuild. It keeps the server that the picker showed, while that server exists. Otherwise it takes the sidebar's server, and otherwise the first server.FinOpsTab.SetServersboth use it. The FinOps tab also re-reads the server list when it opens, and that call now passes the sidebar's server too.RestoreSidebarSelectionsets the sidebar's selection under_suppressSidebarSelection. Then it starts one load of the visible tab, as the selection handler did. The reload and the favorites re-sort both use it.LoadServersAsyncreads both servers after it awaits the server list, so a change made while the list loads is kept.FinOpsTab.SetServersalready read its server after its own await.Every list that the reload rebuilds
ServerList, single-select): it already restored its server by id. It now restores under the guard.AlertsHistoryTab.xaml.cs,JobHistoryTab.xaml.cs). No change.Lite parity
Checked: Lite keeps the selection.
Lite/MainWindow.xaml.csRefreshServerListcallsRefreshServerListon the FinOps and Recommendations tabs, and each one restores the selected server by id. The Lite sidebar never moves those pickers (Lite/MainWindow.SidebarTree.cs). No Lite change.Tests
How the tests ran RED: commit 3cce5dd adds the tests and the seam. There, the seam returns what a rebuild did before this fix: the sidebar's server. At that commit, 4 of the 7 new tests in
ViewerServerSetSyncTestsfailed:Reload_RebuildsTheRecommendationsPicker_KeepingItsServerById:Assert.DoesNotMatch() Failure: Match foundReload_AndTheFavoritesResort_RestoreTheSidebar_WithoutMovingThePickers:Assert.Matches() Failure: Pattern not found in valueFinOpsPicker_KeepsItsServer_AndFallsBackToTheSidebarsServer_AtBothOfItsRebuilds:Assert.Matches() Failure: Pattern not found in valuePicker_KeepsItsServer_WhileItExists_WhereverTheSidebarIs:Assert.Equal() Failure: Values differWhat this RED proves: the three source tests fail on dev's own shell source, which commit 3cce5dd leaves unchanged. The fourth test fails against the seam's stub, so it proves only the seam's contract. The UIA check below proves the behavior. Commit 23ea3da is the fix.
Round 2 adds two source tests:
Reload_ReadsTheServersItKeeps_AfterItsAwait: commit 9ee6d1e adds it. It fails on the shell source of 23ea3da with "LoadServersAsync must read the sidebar's server after its await and before it rebinds the sidebar". Commit 7dd584a is the fix.EverySidebarSelectionWrite_RunsUnderTheSuppressionGuard: it scans every viewer source file. Each write toServerList.SelectedItem,SelectedIndexorSelectedValuemust come after_suppressSidebarSelection = true, with no reset between them. It passes on this branch. With one unguardedServerList.SelectedItem = ...added to the server search handler, it fails with "a sidebar selection write outside the guard: MainWindow.xaml.cs:998".Guards, which pass at both commits:
Picker_WhoseServerWasRemoved_FollowsTheSidebar_AsOnAFreshLoad,FreshLoad_StartsThePicker_OnTheSidebarsServerandNothingToKeepOrFollow_TakesTheFirstServer_AndAnEmptyListSelectsNothing. The existingViewerFinOpsPickerRefreshPinTestspass with no change.UIA check, with the Viewer built from this branch, on a local store with three SQL Server targets:
--add-server). After the reload ("Servers: 3"), both pickers still showed B.Only the source test covers the favorites path, because a favorite toggle writes the user's viewer settings file.
Full suite at 7dd584a: Darling.Tests 19879 total, 0 failed, 1320 skipped (they need a live PostgreSQL store), 1 not run. This PR changes no Lite or shared code.
CHANGELOG
No entry in this PR; the release owner adds it under [Unreleased].
Proposed entry: