Collapse the sidebar dot onto the status the Overview card renders (#2458) - #2470
Conversation
|
Lite/Darling parity: the fixed drift still has a live copy in Darling.Viewer This PR collapses Lite's sidebar dot ( That's not quite true — public string DotStatus => IsOnline switch
{
true => HasCollectorErrors ? "Warning" : "Online",
false => "Offline",
_ => "Unknown"
};It's bound the same way Lite's used to be — Given the pattern in this repo of doing this collapse one surface at a time, this may be intentionally deferred, but flagging it now so it doesn't get lost — and so the "last copy" claim in the PR description gets corrected before it's read as "done everywhere." Otherwise the change itself looks solid: |
ServerConnection.DotStatus computed "Unknown"/"Online"/"Warning"/"Offline" from its own (IsOnline, HasCollectorErrors) pair -- the same four-word ladder the Overview card derives, on a different type, on a different surface, from a different instance of the same flags. The two could say different things about one server and nothing would notice. That is the drift #2429 collapsed the viewer's card for and #2451 collapsed Lite's, and this is the last of them in Lite. Not the last of them anywhere, and an earlier draft of this message said so. Review caught it: DarlingServer.DotStatus in the Darling viewer is a fifth copy, and unlike this one it is not merely capable of disagreeing with its card -- on dev it already does. Both surfaces there classify the SAME ServerFreshness, and a never-collected server draws a grey "Unknown" dot beside an amber "Awaiting first collection" card. That is a different app, a different enum and a different vocabulary decision, so it is filed rather than folded in here. The collapse is not "ServerConnection switches on the pair too, but returns an enum". A second switch is a second ladder however it is typed, so the ladder itself moved into one function: ServerCardStatusRules.Classify, with Word() and Headline() beside it. ServerSummaryItem.CardStatus and ServerConnection.CardStatus both call it, and StatusDisplay/DotStatus and StatusHeadline/DotTooltip are renderings of the result. The card's brushes stay where they are -- hex colours are the card's business and the sidebar paints from theme resources -- but they still switch on CardStatus, not on the flags. The dot gets the tooltip the card's dot got in #2451, opening on the same sentence word for word. It is the thing a reader points at first, which is #2422's complaint one surface over from where it was reported. Its middle line is the part this surface specifically needs. #2457 deliberately kept collection freshness OUT of the status word and gave it its own banded row on the card; the sidebar has no such row and ServerConnection carries no last-collection time to build one from, so a green dot here is a connection answer read somewhere that offers no freshness answer at all. The tooltip says so and names where the freshness answer lives, rather than leaving a reader to infer one from the other -- which is the #2429/#2422 conflation in miniature. Classify takes two arguments and a test names that signature, so folding freshness in cannot happen by accident. #2451's pin moved with the ladder rather than being loosened, and it got wider, because the way it missed this copy is instructive. Assert.Equal(1, CountOccurrences(source, "IsOnline switch")) counted one literal in one file. The fourth copy was in another file AND was written as a chain of if statements rather than a switch, so it evaded that pin on both axes at once and went on evading it through #2451 and #2457. The count now spans both files and pins the classifier's own parameter list; and a new assertion holds the invariant the count was standing in for, syntax-agnostically -- the four words are string literals that appear in exactly one function, and none of them is written in ServerConnection.cs any more. Verification. Lite.Tests targets net10.0-windows and cannot run on macOS, so the logic was run for real anyway: a throwaway net10.0 harness splices ServerCardStatus, ServerCardStatusRules, ServerSummaryItem and the ServerConnection members straight out of the shipped files (a generator reads the source and emits them -- never retyped) behind a System.Windows.Media shim, with a real project reference to PerformanceMonitor.Common. All 35 checks pass, including every reachable (IsOnline, HasCollectorErrors) combination agreeing between the two surfaces. Against dev the strongly-typed harness does not compile at all, so the comparison is run as the identically-expressible subset with the new members reached reflectively: 7 passed / 6 failed on dev, 13 / 0 on the branch. The 7 that pass on both are the controls, and they are the ones a wrong fix breaks first -- the two ladders agree TODAY, which is why this defect is drift potential rather than a live disagreement, and why the assertions that separate the branches are about construction rather than output. The source- and XAML-scanning half was simulated in Python against both versions: 13 fail on dev and all 18 pass on the branch. Whole solution builds, 0 errors. CHANGELOG deliberately untouched -- #2395 is an open release-prep PR that owns that file for 3.5.1. One thing noted and not fixed here: Lite/Mcp/McpDiscoveryTools.cs renders a THIRD vocabulary for the same axis -- "Online"/"Offline"/"Status not checked" -- off ConnectionStatus.IsOnline. It is a different type, it never reads collector health so it has no "Warning" state, and the MCP text is a consumer API, so changing its words is a decision rather than a cleanup. Filed as a separate concern rather than folded in. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
861bf7d to
f1b9a5e
Compare
|
Verified, and you were right to insist the claim get corrected — it turned out to be worse than duplication.
So it is not drift potential there the way it was in Lite — it is a live disagreement on Filed as #2473 with the vocabulary decision it needs made first (what a dot should say for "queued, not dead" — the card chose amber and wrote down why; matching it is the default but worth saying out loud). Not folded in here because it is a different app, a different five-member The overclaim is corrected in both the commit message and the description. Thanks for catching it — it is the same shape of error #2451 got caught on, which is the reason the pin in this PR is written the way it is. |
| (#2031: a silenced server must not look healthy-quiet). --> | ||
| <StackPanel Grid.Column="0" Grid.RowSpan="2" | ||
| Orientation="Horizontal" VerticalAlignment="Center"> | ||
| <!-- #2458: the dot renders the same ServerCardStatus the Overview card does, and |
There was a problem hiding this comment.
The leading comment on the StackPanel two lines above this (``, line 68) still says the dot is about "collection freshness." That's now directly contradicted by the DotTooltip text this PR wires up: "It reports the connection check only, not collection freshness — the Overview card's Last Collect row bands that." Since this file is already being edited to make that exact distinction, it'd be worth fixing the stale leading comment too (e.g. "Status dot (connection) + ...") so a reader doesn't get pointed the wrong way by the first line above the block.
There was a problem hiding this comment.
Fixed. Worth more than a nit, I think — it is #2422's complaint arriving through the comment rather than through the UI: the first line a reader met above that block sent them to the wrong axis.
It is true in the Darling viewer, where the dot genuinely is freshness-derived, and it came over with the rest of the ported row template. In Lite, IsOnline is ServerManager.GetConnectionStatus. The label now says which one it is and records that it used to say the other, because a reader who knows the viewer's version will otherwise assume the two match — which is the same wrong inference the tooltip's middle line exists to prevent.
ReviewWent through the diff against Correctness: No other call sites missed. Grepped the whole tree for Lite/Darling parity: The PR body discloses that Darling's viewer has a fifth, still-divergent copy of this ladder ( One nit: left an inline comment on No security, performance, or null-handling concerns — this is UI-layer string/enum plumbing with no I/O, no user input, and cheap computed properties. |
Review's nit, and it is worth more than a nit: the comment two lines above the status dot called it "Status dot (collection freshness)" -- flatly contradicted by the tooltip this PR just wired underneath it, which says the dot reports the connection check and NOT collection freshness. It is true in the Darling viewer, where the dot really is freshness-derived, and it came over with the rest of the row template. In Lite, IsOnline is ServerManager's live connection check. So the first line a reader met above this block sent them to the wrong axis, which is #2422's complaint arriving via the comment rather than via the UI. The label now says which it is and records that it used to say the other, since a reader who knows the viewer's version will assume the two match. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Reviewed. This is a clean, well-scoped collapse of the fourth copy of the status ladder — no correctness issues found. Verified:
Lite/Darling parity: this PR only touches Lite, but it doesn't quietly leave a Darling gap unaddressed — the description explicitly surfaces that the Darling viewer has its own (pre-existing, Test coverage ( No blocking findings. |
Fixes #2458. Part 2 of #2452; part 1 was #2457, and the type this collapses onto arrived in #2451.
The last copy of the ladder
Lite/Models/ServerConnection.cs:199computed"Unknown"/"Online"/"Warning"/"Offline"from its own(IsOnline, HasCollectorErrors)pair — the same four-word ladder the Overview card derives, on a different type, on a different surface, from a different instance of the same flags. The sidebar and the card could say different things about one server and nothing would notice. That is the drift #2429 collapsed the viewer's card for and #2451 collapsed Lite's; this was the fourth copy, and #2451's own PR body named it as the next place it could go wrong.It is the last of them in Lite, and not the last of them anywhere — an earlier draft of this description said otherwise and review was right to correct it. See the last section.
The collapse is the ladder, not the return type
The change that would have looked like a fix and wasn't: give
ServerConnectionaCardStatus => IsOnline switch { … }and haveDotStatusrender it. That is still a second ladder — the same flag pair read in a second place, just typed better — and it can still drift from the card's.So the ladder itself moved into one function:
ServerSummaryItem.CardStatusandServerConnection.CardStatusboth callClassify.StatusDisplayandDotStatusare bothCardStatus.Word();StatusHeadlineand the first line ofDotTooltipare bothCardStatus.Headline(). The card's brushes stay where they are — hex colours are the card's business, and the sidebar paints its dot from theme resources through XAMLDataTriggers — but they still switch onCardStatusrather than on the flags, exactly as #2451 left them.HasCollectorErrorsisbool?onServerConnectionandboolon the card, so "not yet determined" folds to false. That is the string ladder's own reading, kept deliberately rather than inherited by accident: a server whose collector health nobody has established is not a server reporting failing collectors, and painting it amber would say it was.The dot says what it means, and says what it is not about
The sidebar
Ellipsegets the tooltip the card's dot got in #2451, opening on the same sentence word for word. It is the thing a reader points at first, which is @ehaar's #2422 complaint one surface over from where it was reported.The middle line is what this surface specifically needs, and it is #2457's separation being preserved rather than restated. #2457 kept collection freshness out of the status word and gave it its own banded row on the card, because folding it in recreates the #2429/#2422 conflation of a stale collection with a failing one. The sidebar has no such row and
ServerConnectioncarries no last-collection time to build one from — so a green dot here is a connection answer being read somewhere that offers no freshness answer at all. Naming that, and naming where the freshness answer lives, is what stops a reader inferring one from the other.Classifytakes two arguments andTheDot_CannotBandFreshnessEvenByAccidentpins that signature, so freshness cannot be folded in without changing a line a test names. The failure mode being guarded is a plausible, well-meant edit, not a typo.The closing line names the gesture this surface supports:
ServerListView_MouseDoubleClickconnects, a single click only selects. Same sentence as the card's with the noun the reader is pointing at.The pin moved with the ladder, and got wider — because of how it missed this
#2451 wrote
Assert.Equal(1, CountOccurrences(source, "IsOnline switch"))specifically so a second copy could not reappear. It was true, it stayed true, and the copy it existed to forbid was already there:ifstatements, not aswitch, so even a repo-wide scan for that literal would have found nothing.It evaded the pin on both axes at once, and went on evading it through #2451 and #2457. Loosening it was never on the table; it needed to be pointed at the right invariant.
The count now spans both files and pins the classifier's own parameter list:
and
TheFourWords_AreWrittenInExactlyOnePlaceholds the invariant the count was standing in for, syntax-agnostically: the four words are string literals that appear in exactly one function, and none of them is written inServerConnection.csany more. Anifchain, a switch, a dictionary and a ternary all have to spell them. Comments are stripped first, becauseDotStatus's own doc comment legitimately names all four while the code writes none.Verification
Lite.Teststargetsnet10.0-windowsand cannot run on macOS, so the logic was run for real anyway: a throwawaynet10.0harness splicesServerCardStatus,ServerCardStatusRules,ServerSummaryItemand theServerConnectionmembers straight out of the shipped files (a generator reads the source and emits them — never retyped) behind aSystem.Windows.Mediashim, with a real project reference toPerformanceMonitor.Common. 35 checks, 0 failures, including every reachable(IsOnline, HasCollectorErrors)combination agreeing between the two surfaces on state, on word, and on the tooltip's first line.Against
devthat harness does not compile at all, because none of the members exist there — so the comparison runs as the identically-expressible subset with the new members reached reflectively:The 7 that pass on both sides are the controls, and they are the ones a wrong fix breaks first. Six of them are the two ladders agreeing on every flag combination today — which is the honest shape of this defect: it is drift potential, not a live disagreement. That is precisely why the assertions separating the branches are about construction rather than about output, and why a behavioural test alone could never have justified this change.
The seventh control is #2457's: a card whose collection stopped four hours ago still reads a green "Online", so this is not option 2 sneaking in through the sidebar.
The XAML half has to text-scan for the reason #2429 gave — a
ToolTipattribute lives where no assertion about a C# object can reach it, and removing it compiles perfectly clean.Whole solution builds, 0 errors. CHANGELOG deliberately untouched — #2395 is an open release-prep PR that owns that file for 3.5.1.
Two things noted and not fixed here
The Darling viewer has a fifth copy, and its two surfaces already disagree. Raised by review, verified, and it is worse than duplication.
DarlingServer.DotStatus(Darling/PerformanceMonitor.Darling.Viewer/ViewerDataService.cs:116) derives the same four words from its own flag pair, bound the same way Lite's was. Both it and the viewer's Overview card are stamped from the sameClassifyFreshness(LastCollectionTime, nowUtc)call — and they answer differently for a never-collected server:ServerFreshness.NeverCollectedIsOnline = null→"Unknown"→ grey muted dotAwaitingFirstCollection = true→ "Awaiting first collection", amberThat is on
devtoday, not a latent risk. It is a different app, a differentServerCardStatus(five members,Stalewhere Lite hasCollectorErrors), and a different vocabulary decision — the viewer's word is freshness-derived where Lite's is connection-derived — so folding it in here would mean picking that vocabulary inside a Lite PR. Filed separately.Lite/Mcp/McpDiscoveryTools.cs:28renders a third vocabulary for the same axis —"Online"/"Offline"/"Status not checked"— offConnectionStatus.IsOnline. It is a different type, it never reads collector health so it has no "Warning" state at all, and MCP text is a consumer API whose words downstream automation may key on. Changing them is a decision, not a cleanup, so it is left alone rather than folded in.🤖 Generated with Claude Code