Skip to content

Say why Lite's card is amber, and which axis the word is about (#2437) - #2451

Merged
erikdarlingdata merged 2 commits into
devfrom
fix/2437-lite-card-tooltip
Aug 21, 2026
Merged

Say why Lite's card is amber, and which axis the word is about (#2437)#2451
erikdarlingdata merged 2 commits into
devfrom
fix/2437-lite-card-tooltip

Conversation

@erikdarlingdata

@erikdarlingdata erikdarlingdata commented Aug 21, 2026

Copy link
Copy Markdown
Owner

Part 2 of #2437 — the Lite half. (The web dashboard half is #2447: different language, different risk.)

Lite/MainWindow.xaml:442 was the same bare binding #2429 fixed in the Darling viewer:

<TextBlock Text="{Binding StatusDisplay}" Foreground="{Binding StatusBrush}"
           FontSize="11" Margin="0,0,0,10"/>

A word, a colour, and no way to ask what it meant. A Lite user reads "Warning" and scans the metric rows for the yellow one, exactly as @ehaar had to on the viewer in #2422.

The reason is built from the card's own rows, not recomputed

This is a port, not a wiring change: Lite has no FleetRollup and no BuildReason, so the reason had to be built. The property worth copying carefully is the one that makes the viewer's version work at all — BuildReason is assembled from the card's OWN metric displays, so it cannot drift from the six rows underneath it. A Lite implementation that recomputed severity would satisfy every wording assertion in the test file and still be worse than no tooltip, because the one place it would disagree is a card the reader is staring at.

StatusReason therefore quotes CpuDisplay, BlockingDisplay and DeadlockDisplay verbatim, and is gated on the same predicates the row brushes are painted from. That meant giving each row's "not green" test a name and having both readers use it rather than each carrying its own copy of the threshold:

private bool CpuIsElevated => CpuPercentForAlert >= 50;
public SolidColorBrush CpuBrush => MakeBrush(CpuIsCritical ? "#E57373" : CpuIsElevated ? "#FFB74D" : "#81C784");

and the test asserts the invariant against the shipped brushes, not against a copy of the numbers:

Assert.Equal(card.CpuBrush.Color != green, tooltip.Contains("CPU ", StringComparison.Ordinal));

Memory is deliberately absent. The Memory row carries no severity brush on this card, so there is no band to report — inventing a threshold there is the single thing this property exists to prevent.

Lite's conflation is not the viewer's, and the tooltip has to say so

Checked in source rather than assumed. The viewer derives its status from collection freshness and re-purposes HasCollectorErrors to mean "stale" — its own doc comment says so. Lite's IsOnline comes from _serverManager.GetConnectionStatus(...), a live connection check, and its HasCollectorErrors from _collectorService.GetHealthSummary(server).ErroringCollectors > 0, which counts collectors with ConsecutiveErrors > 0. So Lite does not have the stale-collection conflation — its amber "Warning" is a real collector-failure state.

It has the inverse one instead, and it is arguably sharper. Lite's status word is a connection word, not a health band: it never reflects the metric rows at all, so a card at 96% CPU with two deadlocks reads a green "Online" while its border is red. Fusing the two axes into one clause would reproduce that ambiguity in prose, so the tooltip keeps them on separate lines — what the status word means, then what the rows say.

What the tooltips actually read, run against the shipped source:

card word tooltip
calm Online Online — the last connection check succeeded / Every metric on this card is inside its threshold
CPU 96%, 2 deadlocks Online Online — the last connection check succeeded / Needs attention: CPU 96%, Deadlocks 2
collectors failing Warning Warning — one or more collectors are failing on this server / Every metric on this card is inside its threshold
collectors failing + CPU 62% Warning Warning — one or more collectors are failing on this server / Needs attention: CPU 62%
offline Offline Offline — the last connection check failed
not yet checked Unknown Unknown — this server has not been connection-checked yet / Needs attention: CPU 62%

each closing on Double-click the card to open this server's tab — the viewer's line verbatim, pinned rather than left to coincidence, because the point of doing all three surfaces is that a reader moving between them meets one vocabulary.

Row 2 is the one to look at. That card is in real trouble and the only thing on it that ever said so was a border colour.

Three smaller decisions

An Offline card gets no metric line. The card draws a dimming overlay across those rows precisely because the numbers under it are the last ones collected before the server went dark; demanding attention for them would contradict the card while the reader is looking at it. StatusReason still computes them — the omission is a tooltip decision made in one place, not a hole in what the card knows.

The (IsOnline, HasCollectorErrors) pair now has one reader. The word and the colour already carried their own copy of that ladder and the tooltip would have been a third. ServerCardStatus is #2429's collapse ported; its amber member is named CollectorErrors rather than Stale because that is what it means here, and the difference is written down where the next person will find it.

Review found a fifth reading I had missed, and it turned out to be a live desync rather than a latent one. CardBorderBrush read IsOffline and HasCollectorErrors raw, so on dev a card that has never been connection-checked but carries a collector-error marker draws the amber "collectors failing" border while its word reads Unknown:

status word   : Unknown
border colour : #FFD54F      <- dev
neutral border: #2A2D35

The border and IsOffline (which the offline overlay binds) now render CardStatus too; precedence is unchanged, and the amber border is literally the same amber the word is painted because both render one state. The doc claim that the flag pair has one reader is now asserted rather than left as prose — Assert.Equal(1, CountOccurrences(source, "IsOnline switch")) — which is the assertion that would have caught the overclaim when it was written.

Background="Transparent" is load-bearing, as #2429 found: a TextBlock with a null Background hit-tests on its rendered glyphs alone, so the tooltip would have appeared over the letters of "Warning" and nowhere in the space around them. The status dot carries the same tooltip — same signal, and it is the thing a reader points at first.

Lite has no AwaitingFirstCollection flag — verified before relying on it — so the status/tooltip desync family #2429 spent four review rounds on cannot arise here. Only the unexplained-word half applied.

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 and ServerSummaryItem straight out of the shipped file (never retyped — a generator reads the source and emits it) behind a System.Windows.Media shim, and all 12 tests in the new class pass against it. The table above is that harness's output. Pointed at dev's copy of the same file it does not compile at all, because none of the members exist there.

The source- and XAML-scanning half was simulated in Python against both versions: 20 of 23 assertions fail on dev and all pass on the branch. The three that pass on dev are the two guards that locate the status TextBlock and the status dot, plus the viewer's half of the closing-line parity pin, which this PR does not touch. That half has to text-scan, because a ToolTip attribute lives in XAML 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.

One thing noted and not fixed here: ServerConnection.DotStatus (Lite/Models/ServerConnection.cs:199) is a fourth copy of the same four-word ladder, for the sidebar row. It is a different type on a different surface and unifying it is not this PR's lane, but it is the next place this can drift.

🤖 Generated with Claude Code

Lite/MainWindow.xaml:442 was the same bare Text="{Binding StatusDisplay}"
with no ToolTip that #2429 fixed in the Darling viewer. A reader saw a word in
a colour and had to scan the metric rows guessing which one the card meant, on
every card. That is @ehaar's #2422 question -- "what is it that this text warns
me about?" -- on a second surface.

This is a port rather than a wiring change, because Lite has no FleetRollup and
no BuildReason, so the reason had to be built. The property worth copying
carefully is the one that makes the viewer's version work at all: BuildReason
is assembled from the card's OWN metric displays, so it cannot drift from the
rows underneath it. A Lite implementation that recomputed severity would
satisfy every wording assertion and still be worse than no tooltip, because the
one place it would disagree is a card the reader is staring at.

So StatusReason is built from CpuDisplay, BlockingDisplay and DeadlockDisplay
verbatim, and gated on the SAME predicates the row brushes are painted from --
which meant giving each row's "not green" test a name and having both readers
use it instead of carrying their own copy of the threshold. The test asserts
that against the shipped brushes rather than against a copy of the numbers: for
every fixture, the tooltip names CPU exactly when CpuBrush is not the healthy
green, and likewise for Blocking and Deadlocks. Memory is deliberately absent,
because the Memory row carries no severity brush -- there is no band on this
card to report, and inventing one is the single thing this property exists to
prevent.

Lite's tooltip has one more thing to say than the viewer's, and this is the
part that is not a copy. The viewer's status word is a health BAND; Lite's is a
CONNECTION word. Its "Warning" means ErroringCollectors > 0 -- collectors that
are genuinely failing -- and says nothing about the metric rows, while a card
at 96% CPU with two deadlocks still reads a green "Online" over a red border.
So Lite does NOT have the viewer's stale-collection conflation (Lite's IsOnline
comes from a live connection check, not from freshness), it has the inverse
one, and fusing the two axes into a single clause would reproduce the ambiguity
in prose. The tooltip keeps them on separate lines: what the status word means,
then what the rows say.

An Offline card gets the first line and no metric line. The card draws a
dimming overlay across those rows precisely because the numbers under it are
the last ones collected before the server went dark, and demanding attention
for them would contradict the card while the reader is looking at it.
StatusReason still computes them -- the omission is a tooltip decision made in
one place, not a hole in what the card knows.

The (IsOnline, HasCollectorErrors) pair now has one reader. The word and the
colour already carried their own copy of that ladder and the tooltip would have
been a third; ServerCardStatus is the viewer's collapse ported, and its amber
member is named CollectorErrors rather than Stale because that is what it
actually means here.

Background="Transparent" on the TextBlock is load-bearing rather than
decorative, as #2429 found: a TextBlock with a null Background hit-tests on its
rendered glyphs alone, so the tooltip would have appeared over the letters of
"Warning" and nowhere in the space around them. The status dot carries the same
tooltip, since it is the same signal and the thing a reader points at first.

Lite has no AwaitingFirstCollection flag, which I checked before relying on it,
so the status/tooltip desync family the viewer spent four review rounds on
cannot arise here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Comment thread Lite/Services/LocalDataService.Overview.cs Outdated
@claude

claude Bot commented Aug 21, 2026

Copy link
Copy Markdown

Review summary

Scope: C#/XAML only (Lite's Overview card tooltip), no T-SQL touched, so the T-SQL style/parity checks in CONTRIBUTING.md don't apply here directly.

Correctness. Traced the new CardStatusStatusDisplay/StatusBrush/StatusHeadline/StatusReason/StatusTooltip chain against all five card states (healthy, busy, collector-errors, offline, not-yet-checked) plus the offline+collector-errors combination. The refactor of CpuBrush/BlockingBrush/DeadlockBrush/CardBorderBrush into named gates (CpuIsElevated, CpuIsCritical, BlockingIsElevated, DeadlocksAreElevated) is a faithful behavior-preserving transformation of the prior inline thresholds — no regression found. The offline-card special case (no "Needs attention" line, reason still computed but not surfaced) matches the stated rationale and the test suite. Nullable IsOnline/CpuPercent handling is consistent with pre-existing patterns elsewhere in the file.

Lite/Darling parity. This is intentionally a Lite-only catch-up on work the Darling viewer already got in #2429/#2422, and the PR body is explicit and accurate about where Lite's semantics diverge (connection-word status vs. the viewer's stale-collection conflation, no AwaitingFirstCollection on Lite, wording differences). Compared the closing tooltip line against Darling/PerformanceMonitor.Darling.Viewer/ViewerDataService.Fleet.cs — verbatim match, and it's test-pinned. No parity drift found; the one deliberately-deferred item (ServerConnection.DotStatus as a 4th copy of the status ladder) is already called out by the author in the PR description.

Security / performance. No I/O, network, process, or user-input handling introduced — pure computed UI properties over data that was already being read. Lite.Tests' ReadRepoFile helper reads local repo files via a compile-time [CallerFilePath] walk-up, not user input. No performance concerns; the new properties are cheap getters evaluated on data-binding refresh, same cost class as the code they replace.

Left one non-blocking inline nit on LocalDataService.Overview.cs about a doc comment overclaiming that (IsOnline, HasCollectorErrors) has a single reader, when CardBorderBrush reads both directly rather than through CardStatus. No functional divergence today, just a latent drift risk worth being aware of.

Otherwise this is a clean, well-tested port — nothing blocking.

Review caught this and it is right, and it is worse than the doc-accuracy nit
it was raised as. ServerCardStatus's comment claimed the
(IsOnline, HasCollectorErrors) pair was read in one place; CardBorderBrush read
IsOffline and HasCollectorErrors raw, so it agreed with the status word by
coincidence rather than by construction.

It already disagreed on one pair. A card that has never been connection-checked
but carries a collector-error marker draws the amber "collectors failing"
border -- verified against dev's own copy of the class, which returns #FFD54F
there -- while its word reads "Unknown" and its tooltip says the server has not
been checked yet. That is precisely the desync family this change exists to
close, sitting in the one member it had not reached.

The Overview loader only sets HasCollectorErrors when the connection check
succeeded, so the pair is unreached in practice. That is the argument for making
it unrepresentable rather than for leaving it alone: both flags are plain
settable properties, and "no caller does this today" is what the viewer's two
review rounds on #2429 each disproved.

So the border reads CardStatus, and so does IsOffline, which the offline
overlay binds. Precedence is unchanged -- a dark server, then the metric rows
worst-first, then failing collectors -- and the amber border is now literally
the same amber the status word is painted, because both render one state.

The claim in the doc comment is now true, and a test asserts it rather than
leaving it as prose for the next reviewer to re-check by hand: exactly one
`IsOnline switch` in the file. That is the assertion that would have caught the
overclaim when it was written.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@claude

claude Bot commented Aug 21, 2026

Copy link
Copy Markdown

Reviewed the diff (Lite.Tests/LiteOverviewCardExplainsItselfTests.cs, Lite/MainWindow.xaml, Lite/Services/LocalDataService.Overview.cs). No blocking issues found.

Correctness — traced the CardStatus/StatusDisplay/StatusBrush/CardBorderBrush/StatusReason/StatusTooltip chain by hand against all five ServerCardStatus states (Online, CollectorErrors, Offline, Unknown, plus the Offline+HasCollectorErrors combination). All check out:

  • CardStatus is the single read of (IsOnline, HasCollectorErrors) — confirmed the other four renderings (word, brush, border, IsOffline) all switch on it rather than re-reading the raw flags, so the CardBorderBrush desync fixed in the second commit can't recur elsewhere.
  • Row gates (CpuIsElevated/CpuIsCritical/BlockingIsElevated/DeadlocksAreElevated) are shared between the row brushes and StatusReason, matching the PR's stated invariant that the tooltip can't name a green row or omit a colored one.
  • Confirmed via MainWindow.xaml.cs:729-731 that HasCollectorErrors on ServerSummaryItem is only ever set when connStatus.IsOnline == true, so the Offline+CollectorErrors combination the new test guards against is indeed currently unreachable in practice — the fix is correctly framed as defensive, not a live-bug fix.
  • Nullable handling (IsOnline as bool?, CpuPercentForAlert as double?) is sound; the pattern-match _ => Unknown and nullable comparisons (null >= 50 → false) behave as intended.

Lite/Darling parity — verified against Darling/PerformanceMonitor.Darling.Viewer/ViewerDataService.Fleet.cs: the closing tooltip line ("Double-click the card to open this server's tab") matches verbatim as claimed, and the CardStatus discriminant pattern mirrors the viewer's ServerCardStatus/Headline approach from #2429. The reduced scope (3 metrics vs. the viewer's 6, no FleetRollup/BuildReason, no AwaitingFirstCollection) is real and correctly documented as an intentional asymmetry rather than a drift — Lite genuinely has no equivalent flag/type to port.

Security / perf — no user input, network, file, or process handling introduced; property getters are cheap and only reoccur on WPF binding refresh (unchanged pattern from before this PR).

Nice touch that the second commit is itself a self-caught fix (CardBorderBrush reading the raw flags instead of CardStatus), with a regression test (Assert.Equal(1, CountOccurrences(source, "IsOnline switch"))) pinning that the discriminant has exactly one reader.

@erikdarlingdata
erikdarlingdata merged commit 8f69243 into dev Aug 21, 2026
7 checks passed
erikdarlingdata added a commit that referenced this pull request Aug 21, 2026
…ng green

Lite's Overview card carried five metric rows and a status word and none of them
was collection freshness. The failure mode that leaves is not exotic: the
connection check keeps passing, no collector is *erroring*, and the store takes no
new rows for hours. IsOnline comes from ServerManager.GetConnectionStatus -- a live
check that succeeds whether or not anything is being collected -- and
HasCollectorErrors counts collectors with ConsecutiveErrors > 0, which a collector
that has stopped being scheduled never increments. So the card showed a green
"Online", a neutral border, and a "Last Collect" timestamp painted in the plain
foreground brush, where four hours ago looked exactly like four seconds ago.

Lite reaches that state through its own shipped code. A collector answering
PERMISSIONS sets IsPermissionRestricted and RunCollectorAsync returns BEFORE
LogCollectionAsync, so it writes no collection_log row at all -- and the PERMISSIONS
arm of RecordCollectorResult deliberately does not touch ConsecutiveErrors, because
a permission denial is not a transient failure. A login that loses rights across the
board therefore stops the store dead with ErroringCollectors at zero. The health map
is in-memory as well, so a restart zeroes every streak while the store stays as stale
as the old session left it.

This takes option 1 from the issue, in full. The band is
ServerHealthClassifier.ClassifyFreshness from PerformanceMonitor.Common -- the same
Fresh / Stale / Offline / NeverCollected ladder the Darling viewer bands, off the
same ServerHealthThresholds numbers, and Lite already referenced that project. No
threshold is added to Lite: its fastest configured collector runs every minute, which
is the derivation CollectorCadence documents, so the shared constant is true here on
its own terms rather than borrowed.

Option 2 -- folding freshness into the status word -- is turned down, and the reason
is written on CardStatus so the next reader has the argument in front of them. Lite's
status word is a CONNECTION word and CollectorErrors already means one specific
thing. Folding freshness in would make one amber word mean two unrelated failures,
which is the conflation #2429 spent four review rounds untangling on the viewer and
the reason #2422 was written. So freshness bands its own row and names its own band
there: "08:00:00 (stopped)" in red, with a tooltip that says in words that this is
about collection stopping rather than about the server's metrics.

#2451 landed while this was open and it is what makes the rest of option 1 possible:
the issue asked for the tooltip to "name it, the way it names CPU and Blocking
today", and there is now a StatusReason built out of the card's own row displays to
name it in. CollectionIsNotFresh joins the per-row concern gates beside
CpuIsElevated and BlockingIsElevated, and the clause quotes LastCollectionDisplay
verbatim like every other part there -- so the sentence and the row cannot render
different things, and because the display already carries the band word the clause
says which axis it is about without borrowing a metric's vocabulary.

It goes FIRST in the reason. Collection is the row that says whether the other four
can be believed at all: a card whose collection stopped four hours ago is showing
four-hour-old CPU, and meeting "CPU 4%" before learning that is the wrong order to
be told the two facts in.

The card border escalates too, sharing the amber arm with CollectorErrors, because
collector errors and collector silence are the same class of fault -- the monitoring
of this server is not working -- and silence is the harder of the two to notice,
which is the whole issue. It stays amber even for a stopped collection: red belongs
to a dark server, and a red border under a green "Online" would be the loudest
contradiction on the card.

The band is stamped in GetServerSummaryAsync, the one place a ServerSummaryItem is
built, so the two MCP reads get the same answer the Overview does rather than a fact
that depends on which caller asked. Null means nobody classified it -- unreachable in
the app, reachable from a fixture -- and renders exactly what the row rendered
before: the bare stamp, the card's unknown grey, no tooltip.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
erikdarlingdata added a commit that referenced this pull request Aug 21, 2026
…ctor one

Caught by running #2451's committed test class against this branch rather than
waiting for CI to run it, and CI then failed on exactly this one test and nothing
else.

TheCardBorder_RendersTheSameDiscriminantTheWordDoes pins the border's source text:

    Assert.Contains("CardStatus == ServerCardStatus.CollectorErrors ? \"#FFD54F\"", source)

Folding freshness in as "CardStatus == ServerCardStatus.CollectorErrors ||
CollectionIsNotFresh ? ..." put the new clause between the discriminant and the
colour, so the literal stopped appearing. The pin is a landed guard from another
lane and it is asserting something true and worth asserting -- that the border
reads the same state the word does, which review on #2451 found it had not been
doing -- so the branch moves, not the pin.

Two named causes reading one colour is the better shape anyway: each arm gets its
own line and its own reason, and "the monitoring of this server is not working"
stays one idea expressed as two conditions rather than one condition with a
comment explaining that it is really two.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
erikdarlingdata added a commit that referenced this pull request Aug 21, 2026
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant