Skip to content

Recs rebuild WS1b-1: Recommendations tab (read-only surface)#1061

Merged
erikdarlingdata merged 3 commits into
devfrom
feature/recs-ws1b1-tab
Jun 5, 2026
Merged

Recs rebuild WS1b-1: Recommendations tab (read-only surface)#1061
erikdarlingdata merged 3 commits into
devfrom
feature/recs-ws1b1-tab

Conversation

@erikdarlingdata
Copy link
Copy Markdown
Owner

Scope

The visible, read-only Recommendations tab (Dashboard-only) — WS1b of the recommendations-engine rebuild. Replaces the "Critical Issues" sub-tab with a "Recommendations" tab that renders the unified, de-duped RecommendationItem list (from the WS1a RecommendationsReader, already merged) as the approved layout: a card list grouped into collapsible Critical / Warning / Info sections.

Each card shows: a severity badge (glyph + label, theme status brush), the affected database (bracketed), a headline (Title), wrapped advice (AdviceText), a "Show T-SQL" expander revealing CopyPasteSql in a monospace block with a working Copy button, and Apply + Mute buttons.

Not in scope (deferred to WS1b-2): the Apply and Mute action wiring + the informed-consent gate. Apply + Mute render DISABLED with a tooltip "Available in the next update", so the full approved layout is reviewable with no half-working buttons. Not in scope: Lite (a later PR) — Lite is untouched here.

Visibility predicates (match the alert path)

  • Apply shows only when Remediation != null (engine rows with a built, persisted action).
  • Mute shows only when Source == Engine (the legacy store has no mute concept).

Header bar / actions (these ARE wired)

  • Refresh — re-runs RecommendationsReader.GetRecommendationsAsync for the current server.
  • Generate now — runs an on-demand AnalysisService.AnalyzeAsync(serverId, displayName, hoursBack: 4) for the current server (constructed exactly as AnalysisScheduler does: SqlServerPlanFetcher + AnalysisService, serverId via ServerIdHelper.GetDeterministicHashCode), then refreshes. I wired Generate-now live — the construction path is small and self-contained, so it did not pull in extra scope.
  • Copy (per card) — clipboard write (trivial, wired).

States

  • Loading — the existing LoadingOverlay spinner while reading.
  • Empty — "All clear — no current recommendations."
  • Insufficient data — the engine's own AnalysisService.InsufficientDataMessage (or a default "collecting data — recommendations appear after the engine has ≥24h of history"). Design note: this state is surfaced by Generate now (the engine owns the data-span determination via MinimumDataHours); the read-only Refresh path shows Empty/Loaded. This avoids duplicating the span-check SQL or widening AnalysisService's surface. A freshly-onboarded server therefore shows "All clear" on a plain Refresh and the "collecting data" message after Generate now.

File:line changelog

New files

  • Dashboard/Controls/RecommendationsViewModel.cs — the pure, WPF-free core: RecommendationsViewModel.FromItems/Loading/InsufficientData (groups by CanonicalSeverity into Critical→Warning→Info sections, empty sections omitted, Critical+Warning expanded / Info collapsed), the RecommendationsState enum, and RecommendationCardViewModel / RecommendationSectionViewModel DTOs carrying the display flags + ShowApply/ShowMute predicates.
  • Dashboard/Controls/RecommendationsContent.xaml — the card-list-grouped-by-severity UI (Expander per section, Expander per card for "Show T-SQL"), header bar, and the four mutually-exclusive state regions. All theme DynamicResource brushes; no hardcoded colors (badge text uses a fixed dark glyph color over the colored badge, matching badge-on-status-color convention).
  • Dashboard/Controls/RecommendationsContent.xaml.csInitialize(DatabaseService, ServerConnection, ICredentialService), SetTimeRange(hoursBack), RefreshDataAsync(), GenerateNowButton_Click (live), CopySql_Click (live), and ApplyViewModel state-swap.
  • Dashboard.Tests/RecommendationsViewModelTests.cs — 23 VM unit tests (xUnit v3).

Modified

  • Dashboard/ServerTab.xaml:283-286<TabItem Header="Critical Issues"><controls:CriticalIssuesContent .../><TabItem Header="Recommendations"><controls:RecommendationsContent x:Name="RecommendationsTab"/>.
  • Dashboard/ServerTab.xaml.cs:99RecommendationsTab.Initialize(_databaseService, _serverConnection, _credentialService) (dropped the obsolete InvestigateRequested subscribe/unsubscribe at old :100/:238); :124,:384RecommendationsTab.SetTimeRange(...).
  • Dashboard/ServerTab.Refresh.cs:195RefreshOverviewTabAsync now awaits RecommendationsTab.RefreshDataAsync() (same Overview-tab refresh slot the Critical Issues sub-tab used).
  • Dashboard/ServerTab.TimeRange.cs:233,536,540 — global-time-range + Overview paths now call RecommendationsTab.SetTimeRange(...) / RefreshDataAsync().

CriticalIssuesContent.xaml(.cs) is left intact (unused) per the task. The now-unreferenced OnInvestigateCriticalIssue (in ServerTab.DrillDown.cs) is harmless — no compiler warning, build clean.

Test counts (real)

  • dotnet build PerformanceMonitor.sln -c Debug0 errors (1 pre-existing warning in RemediationTests.cs, unrelated).
  • dotnet test Dashboard.Tests --no-build302 passed, 0 failed (279 baseline + 23 new).
  • dotnet test Lite.Tests360 passed, 0 failed (Lite untouched).
  • New RecommendationsViewModelTests in isolation → 23 passed: grouping into severity sections (order, empty-section omission, intra-band order preserved, expand defaults, header count); state selection (loading / empty / loaded / insufficient-data with engine message + blank-fallback); Apply visibility (only when Remediation != null, false for legacy); Mute visibility (only when Source == Engine); card display flags (HasSql, DatabaseBracketed, SeverityLabel, disabled-tooltip).

Deferred to WS1b-2

  • Apply action wiring + the two-sided informed-consent (RemediationConfirmWindow) gate.
  • Mute action wiring.
  • (Both buttons are present-but-disabled now.)

Needs visual verification (launch the Dashboard)

WPF rendering is not unit-testable. Please launch the Dashboard and confirm: the Recommendations sub-tab (under Overview) renders the grouped collapsible sections; severity badges/colors; the "Show T-SQL" expander + Copy; Apply/Mute appear disabled with the tooltip and per the visibility rules; Refresh and Generate now behave; and the loading / empty / insufficient-data states display correctly.

🤖 Generated with Claude Code

erikdarlingdata and others added 3 commits June 5, 2026 06:49
Replace the Dashboard "Critical Issues" sub-tab with a "Recommendations"
tab rendering the unified, de-duped RecommendationItem list (WS1a reader)
as a card list grouped into collapsible Critical / Warning / Info sections.

Each card shows a severity badge, the affected database, a headline +
advice, a "Show T-SQL" expander with a working Copy button, and Apply +
Mute buttons. Apply shows only when the row carries a built remediation
action (engine rows); Mute shows only for engine-source rows. Apply + Mute
are rendered DISABLED with an "Available in the next update" tooltip — the
action wiring + informed-consent gate are deferred to WS1b-2.

Header bar wires Refresh (re-runs the reader) and Generate now (runs an
on-demand AnalysisService.AnalyzeAsync for the current server, then
refreshes). States: loading spinner, all-clear empty state, and the
engine-owned insufficient-data message (surfaced by Generate now).

Grouping + state selection + Apply/Mute visibility live in a plain,
WPF-free RecommendationsViewModel for unit testing (23 new tests).

Lite untouched (later PR). CriticalIssuesContent left intact (unused).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…w query

Investigate via the tools we already have, not a raw SELECT:
- Incident findings (RecommendationItem.Setting == None: CPU/memory/blocking/
  waits/plan-regression) get "Open in Active Queries -> " (deep-links to the
  Active Queries view scoped to the finding window +/- 1h grace, converting the
  finding's UTC window to server-local since collect.* timestamps are local) and
  "Ask AI" (copies an MCP investigation prompt for the user's robot friend).
- Config-fix findings (Setting != None) get Copy fix / Apply; no deep-link
  (standing misconfig, not an incident).
- Dropped the raw "Show T-SQL"/investigate_query affordance for investigation.

ServerTab handles the deep-link (select Queries tab + QueryPerformanceContent
.ShowActiveQueriesForRange, syncing the global range pickers). RecommendationItem
carries WindowStartUtc/EndUtc; the reader populates them.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…s reader

The legacy critical_issues "pressure/growth" families (Memory Pressure, Memory
Grant Pressure, CPU Scheduling Pressure, Memory Clerk Growth) are noise —
short-window deltas, no quantification, circular investigate queries — and are
covered better by analysis-engine facts. Filter them out of the reader so a
healthy server reads "all clear" instead of showing pressure-noise cards. The
source rules are cut in install/50 in a follow-up.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@erikdarlingdata erikdarlingdata merged commit 171b70e into dev Jun 5, 2026
2 checks passed
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