Recs rebuild WS1a: unified Recommendations reader + de-dupe (data layer)#1060
Merged
Conversation
Foundational data layer for the unified Recommendations surface (no XAML / no WPF control / no tab wiring -- that is WS1b). Dashboard-only. - RecommendationItem VM (+ CanonicalSeverity/RecommendationSource/ RecommendationSetting enums): plain DTO, no WPF dependency, unit-testable. - RecommendationDeduper: PURE static severity mapping + (db, Setting) cross-store de-dupe (C3). AutoShrink/AutoClose collision keeps Engine; QueryStore keeps Legacy; Setting=None never de-dupes. - RecommendationsReader: reads both stores via the existing SqlServerFindingStore.GetRecentFindingsAsync + DatabaseService. GetCriticalIssuesAsync (no new raw SQL), maps each row, de-dupes, sorts. Engine Setting + copy-paste derive from the PERSISTED RemediationAction (drill-down is not returned on read); legacy Setting from the investigate_query ALTER keyword for the two config problem-areas only. - 41 xUnit tests (no DB) covering de-dupe collisions, pass-through, ordering, severity boundary cases, and the setting/copy-paste mappers. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Scope
WS1a of the recommendations-engine rebuild: the data layer only for the unified Recommendations surface. No XAML / no WPF control / no tab wiring (that is WS1b, a later PR), so nothing renders yet — this is foundational, like prior PRs. Dashboard-only.
Builds a unified reader + view-model + de-dupe over the two producers for a server:
config.analysis_findings) read via the existingSqlServerFindingStore.GetRecentFindingsAsync(no new raw SQL for the findings read).config.critical_issues) read via the existingDatabaseService.GetCriticalIssuesAsync.Implements decisions D1 (single surface / hybrid producers), C3 (concrete cross-store de-dupe), D3 (advise/copy-paste + Apply tiering — Apply carried via the persisted action, surfaced in WS1b).
Key design note (verified against the code)
GetRecentFindingsAsyncreturnsremediation_action_json(the builtRemediationAction, D2) but NOT the ephemeralDrillDown. So on read the engineSettingand copy-paste SQL are derived from the persistedRemediationAction(FactKey=="RCSI"→ Rcsi;DbConfigTargets→ AutoShrink/AutoClose/PageVerify), not from a drill-down that is absent. The copy-pasteALTER DATABASEstatements are rebuilt from those persisted targets with the same QUOTENAME-equivalent bracketing + SET-clause literals the executor uses.File:line changelog
New files (4; +1067):
Dashboard/Services/Recommendations/RecommendationItem.cs:1— the unified VM (plain DTO, no WPF dep) + theCanonicalSeverity(Info/Warning/Critical),RecommendationSource(Engine/Legacy),RecommendationSetting(None/AutoShrink/AutoClose/QueryStore/Rcsi/PageVerify) enums. Fields:CanonicalSeverity,RawSeverity,Database,Title,ProblemArea,AdviceText,CopyPasteSql,Remediation(RemediationAction?),Source,StoryPathHash,Setting.Dashboard/Services/Recommendations/RecommendationDeduper.cs:1— the PURE static core:FromEngineSeverity(>=1.5Critical />=0.75Warning / else Info,:30),FromLegacySeverity(text → band,:46),Merge(the de-dupe over two mapped lists,:79),PreferEngineFor(collision precedence,:135),NormalizeDatabase(trim/lower,:170),CompareForDisplay(severity desc total order,:181).Dashboard/Services/Recommendations/RecommendationsReader.cs:1—GetRecommendationsAsync(serverId, serverName, hoursBack, limit)(:59) reads both stores, maps, de-dupes, sorts.MapEngineFinding(:83),MapLegacyIssue(:111),SettingFromAction(:145),SettingFromDbConfig(:177),SettingFromLegacy(ALTER-keyword parse for the two config problem-areas only,:194),BuildCopyPasteFromAction(:225).Dashboard.Tests/RecommendationDeduperTests.cs:1— 41 xUnit tests (no DB).No existing files modified (
git diff --statvs HEAD is empty).De-dupe (C3) summary
Setting).Setting=Nonerows never de-dupe (memory pressure, server-level MAXDOP/CTFP, dumps, CPU/wait findings) — all pass through.Settingfrom the persisted action; legacySettingonly forproblem_area ∈ {Database Configuration, Query Store Configuration}, derived from theinvestigate_queryALTER keyword (AUTO_SHRINK/AUTO_CLOSE/QUERY_STORE) — the free-textmessageis never parsed.Test counts (REAL —
dotnet test --no-build -c Debug)dotnet build PerformanceMonitor.sln -c Debug→ 0 errors (1 pre-existing warning inRemediationTests.cs, not from this PR).RecommendationDeduperTests).New-test coverage: AUTO_SHRINK in both stores same DB → one row Source=Engine; AUTO_CLOSE same; QUERY_STORE in both (synthesized engine QS row) → one row Source=Legacy; same setting different DBs → both kept; memory-pressure legacy (None) + engine CPU finding → both kept; severity boundary cases (0.74→Info, 0.75→Warning, 1.49→Warning, 1.5→Critical, plus the text mapping); plus the setting/copy-paste mapper helpers.
Needs integration verification (live DB — NOT unit-tested here)
RecommendationsReader.GetRecommendationsAsyncagainst a realconfig.analysis_findings+config.critical_issues(the unit tests exercise the pure de-dupe + the mappers with synthesized inputs, never the DB).remediation_action_jsonsuch thatSettingFromAction+BuildCopyPasteFromActionproduce the expectedSettingandALTERtext from the persisted action on read.problem_area+investigate_querykeyword for the de-dupe to fire (and a real per-DB AUTO_SHRINK present in BOTH stores collapses to the single Engine row).🤖 Generated with Claude Code