Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
139 commits
Select commit Hold shift + click to select a range
1da0ceb
Dashboard: visible sub-tab only refresh on auto-refresh ticks (#528)
erikdarlingdata Mar 12, 2026
fe6ce05
Fix FinOps collector scheduling, server switch, and utilization bugs
erikdarlingdata Mar 12, 2026
6d7a1af
Merge pull request #534 from erikdarlingdata/fix/finops-collector-and…
erikdarlingdata Mar 12, 2026
bbe1c63
Fix RetrievedFromCache always showing False (#536)
erikdarlingdata Mar 12, 2026
247a446
Fix SQL dumps on mirroring passive servers from FinOps collectors (#535)
erikdarlingdata Mar 12, 2026
3df331a
Merge pull request #537 from erikdarlingdata/fix/finops-mirror-passiv…
erikdarlingdata Mar 12, 2026
cfd7d6e
Fix installer dropping database on every upgrade (#538, #539)
erikdarlingdata Mar 12, 2026
93948e4
Merge pull request #540 from erikdarlingdata/fix/installer-data-loss-538
erikdarlingdata Mar 12, 2026
be3f387
Add ErikAI analysis engine with MCP tools
erikdarlingdata Mar 13, 2026
1ca2bb2
Add CPU, I/O, TempDB, memory grant, and query fact collectors
erikdarlingdata Mar 13, 2026
368c7ef
Add remaining fact collectors for full table coverage
erikdarlingdata Mar 13, 2026
d8069a9
Add execution plan analysis MCP tools to Dashboard and Lite
erikdarlingdata Mar 13, 2026
c8e1738
Merge pull request #542 from erikdarlingdata/feature/mcp-plan-tools
erikdarlingdata Mar 13, 2026
3202933
Add misery tests, expand test data seeding, fix PLE=0 scoring bug
erikdarlingdata Mar 13, 2026
c20c280
Fix Lite perfmon chart bugs and Dashboard ScottPlot crash handling (#…
erikdarlingdata Mar 13, 2026
c0b1dec
Merge pull request #546 from erikdarlingdata/fix/issue-544-545-perfmo…
erikdarlingdata Mar 13, 2026
70009e6
Fix arithmetic overflow in query_stats collector (#547)
erikdarlingdata Mar 13, 2026
ab0a917
Remove accidentally tracked publish file
erikdarlingdata Mar 13, 2026
5c94d66
Merge pull request #548 from erikdarlingdata/fix/issue-547-smallint-o…
erikdarlingdata Mar 13, 2026
d379bcf
Fix Lite query_stats dop columns to match DMV bigint type (#547)
erikdarlingdata Mar 13, 2026
c47e6f3
Merge pull request #549 from erikdarlingdata/fix/issue-547-lite-dop
erikdarlingdata Mar 13, 2026
2faf768
Fix ensure_collection_table query_stats column types (#547)
erikdarlingdata Mar 13, 2026
a7291d7
Merge pull request #550 from erikdarlingdata/fix/issue-547-ensure-table
erikdarlingdata Mar 13, 2026
b0bb027
Widen collector table columns to match DMV documentation
erikdarlingdata Mar 13, 2026
943ddc9
Merge pull request #552 from erikdarlingdata/fix/audit-column-type-mi…
erikdarlingdata Mar 13, 2026
d0d671d
Fix Index Analysis scrollbar not working with many results (#554)
erikdarlingdata Mar 16, 2026
74554af
Merge pull request #558 from erikdarlingdata/fix/index-analysis-scrol…
erikdarlingdata Mar 16, 2026
ed235d6
Send email alerts when monitored servers go offline/online (#529)
erikdarlingdata Mar 16, 2026
5a08505
Merge pull request #559 from erikdarlingdata/feature/server-unreachab…
erikdarlingdata Mar 16, 2026
b868c10
Collect all Azure SQL DB database sizes, not just master (#557)
erikdarlingdata Mar 16, 2026
5e4c864
Merge pull request #560 from erikdarlingdata/fix/azure-finops-databas…
erikdarlingdata Mar 16, 2026
5b5feb5
Drop HAS_DBACCESS filter for Azure SQL DB database enumeration
erikdarlingdata Mar 16, 2026
b6e129e
Merge pull request #561 from erikdarlingdata/fix/azure-has-dbaccess-557
erikdarlingdata Mar 16, 2026
34d3a4f
Fix Azure SQL DB collectors to query all databases (#557)
erikdarlingdata Mar 16, 2026
548d5fd
Merge pull request #563 from erikdarlingdata/fix/azure-server-invento…
erikdarlingdata Mar 16, 2026
7662ec2
Add column filters to all FinOps DataGrids (#562)
erikdarlingdata Mar 16, 2026
d84a483
Merge pull request #565 from erikdarlingdata/feature/finops-column-fi…
erikdarlingdata Mar 16, 2026
34194c6
Add FinOps dollar-denominated cost attribution (#564)
erikdarlingdata Mar 16, 2026
01382c8
Merge pull request #568 from erikdarlingdata/feature/finops-cost-attr…
erikdarlingdata Mar 16, 2026
c0e5e7c
Add Lite data import feature for upgrading between installs (#566)
erikdarlingdata Mar 16, 2026
822a178
Add NuGet package dependency licenses to third-party notices
HannahVernon Mar 16, 2026
7c11cba
Merge pull request #569 from erikdarlingdata/feature/lite-data-import…
erikdarlingdata Mar 16, 2026
abcf008
Add FinOps Recommendations tab with Phase 1 checks (#564)
erikdarlingdata Mar 16, 2026
8b3e2ca
Merge pull request #571 from erikdarlingdata/feature/finops-recommend…
erikdarlingdata Mar 16, 2026
434d325
Merge dev into feature/erikai — sync with latest changes
erikdarlingdata Mar 16, 2026
150d56c
Fix FinOps recommendations: remove tempdb check, exclude PerformanceM…
erikdarlingdata Mar 16, 2026
6249572
Merge pull request #572 from erikdarlingdata/fix/finops-recommendatio…
erikdarlingdata Mar 16, 2026
ce229c3
Merge pull request #570 from HannahVernon/fix/include-dependency-lice…
erikdarlingdata Mar 16, 2026
e89d64b
Add per-server Utility Database setting for community stored procedur…
erikdarlingdata Mar 16, 2026
615d57c
Merge pull request #573 from erikdarlingdata/feature/utility-database…
erikdarlingdata Mar 16, 2026
b631c7a
Fix edition detection: read from server_properties instead of empty s…
erikdarlingdata Mar 16, 2026
72a1107
Add installer adversarial tests — Phase 1 (#543)
erikdarlingdata Mar 16, 2026
fd8d0d1
Merge pull request #574 from erikdarlingdata/feature/installer-advers…
erikdarlingdata Mar 17, 2026
e9b6da5
Add RCSI-off scoring with reader/writer lock contention amplifiers
erikdarlingdata Mar 17, 2026
7dcbc65
Add LATCH_EX/LATCH_SH thresholds, amplifiers, and graph edges
erikdarlingdata Mar 17, 2026
0585014
Fix analysis dilution: decouple data maturity check from analysis window
erikdarlingdata Mar 17, 2026
c90444c
Add restricted permissions and missing columns adversarial tests (#543)
erikdarlingdata Mar 17, 2026
74ee5f4
Merge pull request #575 from erikdarlingdata/feature/installer-tests-…
erikdarlingdata Mar 17, 2026
133cf24
Add CPU spike detection for bursty workloads
erikdarlingdata Mar 17, 2026
143b6c7
Remove artificial missing-columns test
erikdarlingdata Mar 17, 2026
73d1927
Merge pull request #579 from erikdarlingdata/fix/drop-missing-columns…
erikdarlingdata Mar 17, 2026
c22ded2
Add 8 MCP tools for Lite coverage gaps (#576)
erikdarlingdata Mar 17, 2026
04851db
Add SQL Server version check to both installers (#543)
erikdarlingdata Mar 17, 2026
bbcaed7
Merge pull request #580 from erikdarlingdata/feature/installer-versio…
erikdarlingdata Mar 17, 2026
f2d529f
Add 10 MCP tools for Dashboard coverage gaps (#577)
erikdarlingdata Mar 17, 2026
b71fb64
Fix Enterprise feature audit text — partitioning is not Enterprise-only
erikdarlingdata Mar 17, 2026
f4a0756
Merge pull request #581 from erikdarlingdata/fix/enterprise-feature-text
erikdarlingdata Mar 17, 2026
a7d484d
Add remaining 14 Dashboard MCP tools for full coverage (#577)
erikdarlingdata Mar 17, 2026
3f71548
Update README MCP tool counts and table for full coverage
erikdarlingdata Mar 17, 2026
af0786a
Update README for current project state
erikdarlingdata Mar 17, 2026
2f93554
Merge pull request #583 from erikdarlingdata/fix/readme-updates
erikdarlingdata Mar 17, 2026
83f4184
Add Phase 3 + Phase 4 FinOps recommendations (#564)
erikdarlingdata Mar 17, 2026
c44ed3c
Merge pull request #584 from erikdarlingdata/feature/finops-recommend…
erikdarlingdata Mar 17, 2026
b0565ca
Add self-sufficient drill-down to analyze_server (#578)
erikdarlingdata Mar 17, 2026
8446a86
Fix README MCP tool counts: Dashboard 57, Lite 51
erikdarlingdata Mar 17, 2026
1676e06
Merge pull request #585 from erikdarlingdata/feature/lite-mcp-gaps-576
erikdarlingdata Mar 17, 2026
360ff12
Merge pull request #586 from erikdarlingdata/feature/dashboard-mcp-ga…
erikdarlingdata Mar 17, 2026
06b2924
Merge pull request #587 from erikdarlingdata/feature/analysis-self-su…
erikdarlingdata Mar 17, 2026
5c53623
Merge pull request #588 from erikdarlingdata/feature/erikai
erikdarlingdata Mar 17, 2026
cf42efb
Remove Standard → Express feasibility check
erikdarlingdata Mar 17, 2026
bf69f1c
Merge pull request #591 from erikdarlingdata/fix/remove-express-feasi…
erikdarlingdata Mar 17, 2026
c4a5fca
Fix Dashboard MCP cosmetic issues: latch/spinlock top-N and plan cach…
erikdarlingdata Mar 17, 2026
48ec030
Merge pull request #592 from erikdarlingdata/fix/dashboard-mcp-cosmetics
erikdarlingdata Mar 17, 2026
5962acb
Add bad actor detection — per-query scoring for consistently terrible…
erikdarlingdata Mar 17, 2026
27b2637
Merge pull request #595 from erikdarlingdata/feature/bad-actor-detect…
erikdarlingdata Mar 17, 2026
d683f39
Add FinOps High Impact Queries tab — 80/20 analysis across all resour…
erikdarlingdata Mar 17, 2026
7765d63
Merge pull request #596 from erikdarlingdata/feature/finops-high-impact
erikdarlingdata Mar 17, 2026
ec3da24
Fix High Impact query preview — full text in tooltip, ellipsis in row
erikdarlingdata Mar 17, 2026
b1dd72c
Merge pull request #597 from erikdarlingdata/fix/high-impact-query-text
erikdarlingdata Mar 17, 2026
bc4547d
Fix High Impact query preview to match existing tabs — LEFT(200), pla…
erikdarlingdata Mar 17, 2026
0d0f52d
Merge pull request #598 from erikdarlingdata/fix/high-impact-query-pr…
erikdarlingdata Mar 17, 2026
fc6f5f4
Fix High Impact grid — add query tooltip and Lite column filters
erikdarlingdata Mar 17, 2026
757ac4c
Merge pull request #599 from erikdarlingdata/feature/fix-high-impact-…
erikdarlingdata Mar 17, 2026
f0a5647
Fix query tooltips — show full text, not truncated
erikdarlingdata Mar 17, 2026
8717869
Merge pull request #601 from erikdarlingdata/fix/query-tooltip-full-text
erikdarlingdata Mar 17, 2026
9686ac6
Fix query preview — truncated in row, full text in tooltip
erikdarlingdata Mar 17, 2026
49f20de
Merge pull request #602 from erikdarlingdata/fix/query-preview-proper
erikdarlingdata Mar 17, 2026
44c70c2
Add plan analysis integration for bad actor findings (#594)
erikdarlingdata Mar 17, 2026
17e2eec
Merge pull request #603 from erikdarlingdata/feature/plan-integration…
erikdarlingdata Mar 17, 2026
87bad78
Add CodeRabbit AI code review configuration (#600)
erikdarlingdata Mar 17, 2026
ed82dd0
Add FinOps test infrastructure — scoring extraction and scenario seed…
erikdarlingdata Mar 17, 2026
5c08471
Merge pull request #605 from erikdarlingdata/feature/finops-test-seeder
erikdarlingdata Mar 17, 2026
3b43644
Add anomaly detection — baseline comparison for acute deviations (#589)
erikdarlingdata Mar 17, 2026
f1f3fe9
Merge pull request #606 from erikdarlingdata/feature/anomaly-detectio…
erikdarlingdata Mar 17, 2026
872ec22
Add VM right-sizing, storage tier optimization, and reserved capacity…
erikdarlingdata Mar 17, 2026
af8cef0
Merge pull request #607 from erikdarlingdata/feature/finops-phase3-re…
erikdarlingdata Mar 17, 2026
646e0ec
On-demand plan fetch for bad actor findings (#604)
erikdarlingdata Mar 17, 2026
1b3a47f
Merge pull request #609 from erikdarlingdata/feature/on-demand-plan-f…
erikdarlingdata Mar 17, 2026
f1c2cb4
Add FinOps xunit tests — scenario-based validation of recommendations…
erikdarlingdata Mar 17, 2026
5c679e8
Merge pull request #610 from erikdarlingdata/feature/finops-xunit-tests
erikdarlingdata Mar 17, 2026
b0bbb41
Fix 6 verified Lite bugs from code review (#611)
erikdarlingdata Mar 17, 2026
91ed3e3
Fix GUI installer encryption mapping and history logging (#612)
erikdarlingdata Mar 17, 2026
2c1b840
Add HealthCalculator + PercentRank tests, fix PercentRank >1.0 bug
erikdarlingdata Mar 17, 2026
713f840
Merge pull request #613 from erikdarlingdata/fix/health-calculator-tests
erikdarlingdata Mar 17, 2026
619b003
Add column filters to FinOps grids (Dashboard + Lite)
erikdarlingdata Mar 17, 2026
ba9b20c
Merge pull request #614 from erikdarlingdata/feature/finops-grid-filters
erikdarlingdata Mar 17, 2026
bbe764d
Port ErikAI analysis engine to Dashboard (#590)
erikdarlingdata Mar 17, 2026
e858436
Merge pull request #615 from erikdarlingdata/feature/dashboard-erikai…
erikdarlingdata Mar 17, 2026
9269347
Add filters to Dashboard IdleDatabases, TempDB, and Index Analysis grids
erikdarlingdata Mar 18, 2026
b0c1b2a
Merge pull request #616 from erikdarlingdata/fix/dashboard-remaining-…
erikdarlingdata Mar 18, 2026
9a4c3a7
Hide IdleDatabases grid when empty to fix scrollbar artifact
erikdarlingdata Mar 18, 2026
b1507e9
Merge pull request #617 from erikdarlingdata/fix/idle-db-scrollbar
erikdarlingdata Mar 18, 2026
33aae62
Fix Dashboard drill-down: Unicode arrow in story path split
erikdarlingdata Mar 18, 2026
d024ecd
Merge pull request #618 from erikdarlingdata/fix/dashboard-drilldown-…
erikdarlingdata Mar 18, 2026
bd654fd
Fix TempDB grid: hide when empty, restore Warning column Width=*
erikdarlingdata Mar 18, 2026
ff04cf7
Merge pull request #619 from erikdarlingdata/fix/tempdb-scrollbar-war…
erikdarlingdata Mar 18, 2026
99d5bbb
Hide all empty DataGrids to prevent white scrollbar artifacts
erikdarlingdata Mar 18, 2026
887da5f
Merge pull request #620 from erikdarlingdata/fix/hide-empty-grids
erikdarlingdata Mar 18, 2026
0364691
Fix FinOps grid scrollbars — disable horizontal scroll on all grids
erikdarlingdata Mar 18, 2026
b06f37b
Merge pull request #621 from erikdarlingdata/fix/disable-horizontal-s…
erikdarlingdata Mar 18, 2026
36ded93
Fix Add Server dialog buttons hidden when SQL auth selected
erikdarlingdata Mar 18, 2026
afb614f
Merge pull request #622 from erikdarlingdata/fix/add-server-dialog-he…
erikdarlingdata Mar 18, 2026
6b25e8b
Version bump to 2.3.0 + fix GUI uninstall button state
erikdarlingdata Mar 18, 2026
20919ec
Merge pull request #623 from erikdarlingdata/release-prep/v2.3.0
erikdarlingdata Mar 18, 2026
8b2703d
Add 2.3.0 changelog entry
erikdarlingdata Mar 18, 2026
e438b9d
Merge pull request #624 from erikdarlingdata/release-prep/changelog-2…
erikdarlingdata Mar 18, 2026
7fd992b
Add growth rate and VLF count columns (#567) (#625)
erikdarlingdata Mar 18, 2026
54334b4
Fix FinOps server dropdown missing (Read-Only) suffix
erikdarlingdata Mar 18, 2026
6722fb7
Merge pull request #626 from erikdarlingdata/fix/finops-readonly-label
erikdarlingdata Mar 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions .coderabbit.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

language: "en-US"
early_access: false
enable_free_tier: true

reviews:
profile: "chill"
high_level_summary: true
review_status: true
commit_status: true
collapse_walkthrough: true
sequence_diagrams: false
poem: false

path_filters:
- "!**/*.Designer.cs"
- "!**/bin/**"
- "!**/obj/**"
- "!**/publish/**"
- "!**/*.user"
- "!**/*.suo"

path_instructions:
- path: "Dashboard/**/*.cs"
instructions: >
This is a WPF .NET 8 desktop app (Dashboard) that reads from SQL Server.
Uses data binding, async/await patterns, and INotifyPropertyChanged.
Watch for: null reference risks, disposal of SQL connections,
thread safety with UI dispatch, and proper async patterns.
- path: "Lite/**/*.cs"
instructions: >
This is a WPF .NET 8 desktop app (Lite) that collects SQL Server DMV data
into a local DuckDB database. Uses ReaderWriterLockSlim for DB coordination.
Watch for: connection disposal, thread safety, DuckDB access patterns,
and proper async/await usage.
- path: "**/*.sql"
instructions: >
T-SQL stored procedures and scripts for SQL Server.
Watch for: SQL injection risks, missing error handling (TRY/CATCH),
proper use of SET NOCOUNT ON, and parameter sniffing concerns.
- path: "Installers/**"
instructions: >
WiX-based MSI installer projects. Be cautious about upgrade paths
and file versioning. Schema upgrades go in upgrades/ folder, not install scripts.

auto_review:
enabled: true
drafts: false
base_branches:
- "dev"
- "main"

tools:
gitleaks:
enabled: true
github-checks:
enabled: true

chat:
auto_reply: true

knowledge_base:
learnings:
scope: "local"
pull_requests:
scope: "local"
81 changes: 81 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,87 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [2.3.0] - 2026-03-18

### Important

- **Schema upgrade**: Six columns widened across three tables (`query_stats`, `cpu_scheduler_stats`, `waiting_tasks`, `database_size_stats`) to match DMV documentation types. These are in-place ALTER COLUMN operations — fast on any table size, no data migration. Upgrade scripts run automatically via the CLI/GUI installer.
- **SQL Server version check**: Both installers now reject SQL Server 2014 and earlier before running any scripts, with a clear error message. Azure MI (EngineEdition 8) is always accepted. ([#543])
- **Installer adversarial tests**: 35 automated tests covering upgrade failures, data survival, idempotency, version detection fallback, file filtering, restricted permissions, and more. These run as part of pre-release validation. ([#543])

### Added

- **ErikAI analysis engine** — rule-based inference engine for Lite that scores server health across wait stats, CPU, memory, I/O, blocking, tempdb, and query performance. Surfaces actionable findings with severity, detail, and recommended actions. Includes anomaly detection (baseline comparison for acute deviations), bad actor detection (per-query scoring for consistently terrible queries), and CPU spike detection for bursty workloads. ([#589], [#593])
- **ErikAI Dashboard port** — full analysis engine ported to Dashboard with SQL Server backend ([#590])
- **FinOps cost optimization recommendations** — Phase 1-4 checks: enterprise feature audit, CPU/memory right-sizing, compression savings estimator, unused index cost quantification, dormant database detection, dev/test workload detection, VM right-sizing, storage tier optimization, reserved capacity candidates ([#564])
- **FinOps High Impact Queries** — 80/20 analysis showing which queries consume the most resources across all dimensions ([#564])
- **FinOps dollar-denominated cost attribution** — per-server monthly cost setting with proportional database-level breakdown ([#564])
- **On-demand plan fetch** for bad actor and analysis findings — click to retrieve execution plans for flagged queries ([#604])
- **Plan analysis integration** — findings include execution plan analysis when plans are available ([#594])
- **Server unreachable email alerts** — Dashboard sends email (not just tray notification) when a monitored server goes offline or comes back online ([#529])
- **Column filters on all FinOps DataGrids** — filter funnel icons on every column header across all 7 FinOps grids in Lite and Dashboard ([#562])
- **Column filters on Dashboard** IdleDatabases, TempDB, and Index Analysis grids
- **Lite data import** — "Import Data" button brings in monitoring history from a previous Lite install via parquet files, preserving trend data across version upgrades ([#566])
- **Per-server Utility Database setting** — Lite can call community stored procedures (sp_IndexCleanup) from a database other than master ([#555])
- **SQL Server version check** in both CLI and GUI installers — rejects 2014 and earlier with a clear message ([#543])
- **Execution plan analysis MCP tools** for both Dashboard and Lite
- **Full MCP tool coverage** — Dashboard expanded from 28 to 57 tools, Lite from 32 to 51 tools ([#576], [#577])
- **Self-sufficient analyze_server drill-down** — MCP tool returns complete analysis, not breadcrumb trail ([#578])
- **NuGet package dependency licenses** in THIRD_PARTY_NOTICES.md

### Changed

- **Azure SQL DB FinOps** — all collectors (database sizes, query stats, file I/O) now connect to each database individually instead of only querying master. Server Inventory uses dynamic SQL to avoid `sys.master_files` dependency. ([#557])
- **Index Analysis scroll fix** — both summary and detail grids now use proportional heights instead of Auto, so they scroll independently with large result sets ([#554])
- **Dashboard Add Server dialog** — increased MaxHeight from 700 to 850px so buttons are visible when SQL auth fields are shown
- **GUI installer** — Uninstall button now correctly enables after a successful install
- **GUI installer** — fixed encryption mapping and history logging ([#612])
- **Dashboard visible sub-tab only refresh** on auto-refresh ticks ([#528])
- Analysis engine decouples data maturity check from analysis window

### Fixed

- **Installer dropping database on every upgrade** — `00_uninstall.sql` excluded from install file list, installer aborts on upgrade failure, version detection fallback returns "1.0.0" instead of null ([#538], [#539])
- **SQL dumps on mirroring passive servers** from FinOps collectors ([#535])
- **RetrievedFromCache** always showing False ([#536])
- **Arithmetic overflow** in query_stats collector for dop/thread columns ([#547])
- **Lite perfmon chart bugs** and Dashboard ScottPlot crash handling ([#544], [#545])
- **PLE=0 scoring bug** — was scored as harmless, now correctly flagged ([#543])
- **PercentRank >1.0** bug in HealthCalculator
- **6 verified Lite bugs** from code review ([#611])
- **Enterprise feature audit text** — partitioning is not Enterprise-only
- **FinOps collector scheduling**, server switch, and utilization bugs
- **Dashboard drill-down** Unicode arrow in story path split
- **Empty DataGrid scrollbar artifacts** — hide grids when empty across all FinOps tabs
- **Query preview** — truncated in row, full text in tooltip

[#529]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/529
[#535]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/535
[#536]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/536
[#538]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/538
[#539]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/539
[#543]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/543
[#544]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/544
[#545]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/545
[#547]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/547
[#554]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/554
[#555]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/555
[#557]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/557
[#562]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/562
[#564]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/564
[#566]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/566
[#576]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/576
[#577]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/577
[#578]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/578
[#528]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/528
[#589]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/589
[#590]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/590
[#593]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/593
[#594]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/594
[#604]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/604
[#611]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/611
[#612]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/612

## [2.2.0] - 2026-03-11

**Contributors:** [@HannahVernon](https://github.com/HannahVernon), [@ClaudioESSilva](https://github.com/ClaudioESSilva), [@dphugo](https://github.com/dphugo), [@Orestes](https://github.com/Orestes) — thank you!
Expand Down
10 changes: 9 additions & 1 deletion Dashboard/AddServerDialog.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Add SQL Server"
SizeToContent="Height" Width="450" MaxHeight="700"
SizeToContent="Height" Width="450" MaxHeight="850"
WindowStartupLocation="CenterOwner"
ResizeMode="NoResize"
Background="{DynamicResource BackgroundBrush}"
Expand Down Expand Up @@ -100,6 +100,14 @@
<CheckBox x:Name="IsFavoriteCheckBox" Content="Mark as favorite (appears at top of list)"
Foreground="{DynamicResource ForegroundBrush}" Margin="0,0,0,6"/>

<!-- Monthly Cost -->
<StackPanel Orientation="Horizontal" Margin="0,0,0,6">
<TextBlock Text="Monthly Cost ($):" Width="110" VerticalAlignment="Center"
Foreground="{DynamicResource ForegroundBrush}"/>
<TextBox x:Name="MonthlyCostTextBox" Width="100" TextAlignment="Right" Text="0"
ToolTip="What this server costs per month (license, compute, storage combined). Used for FinOps cost attribution. Leave 0 to hide cost columns."/>
</StackPanel>

<!-- Description -->
<TextBlock Text="Description (optional):" Margin="0,4,0,4"
Foreground="{DynamicResource ForegroundBrush}"/>
Expand Down
10 changes: 9 additions & 1 deletion Dashboard/AddServerDialog.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public AddServerDialog(ServerConnection existingServer)
ServerNameTextBox.Text = existingServer.ServerName;
DescriptionTextBox.Text = existingServer.Description;
IsFavoriteCheckBox.IsChecked = existingServer.IsFavorite;
MonthlyCostTextBox.Text = existingServer.MonthlyCostUsd.ToString(System.Globalization.CultureInfo.InvariantCulture);

// Load encryption settings
EncryptModeComboBox.SelectedIndex = existingServer.EncryptMode switch
Expand Down Expand Up @@ -328,9 +329,15 @@ private async void Save_Click(object sender, RoutedEventArgs e)
ServerConnection.IsFavorite = IsFavoriteCheckBox.IsChecked == true;
ServerConnection.EncryptMode = GetSelectedEncryptMode();
ServerConnection.TrustServerCertificate = TrustServerCertificateCheckBox.IsChecked == true;
if (decimal.TryParse(MonthlyCostTextBox.Text, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out var editCost) && editCost >= 0)
ServerConnection.MonthlyCostUsd = editCost;
}
else
{
decimal monthlyCost = 0m;
if (decimal.TryParse(MonthlyCostTextBox.Text, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out var newCost) && newCost >= 0)
monthlyCost = newCost;

ServerConnection = new ServerConnection
{
DisplayName = displayName,
Expand All @@ -341,7 +348,8 @@ private async void Save_Click(object sender, RoutedEventArgs e)
CreatedDate = DateTime.Now,
LastConnected = DateTime.Now,
EncryptMode = GetSelectedEncryptMode(),
TrustServerCertificate = TrustServerCertificateCheckBox.IsChecked == true
TrustServerCertificate = TrustServerCertificateCheckBox.IsChecked == true,
MonthlyCostUsd = monthlyCost
};
}

Expand Down
152 changes: 152 additions & 0 deletions Dashboard/Analysis/AnalysisModels.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
using System;
using System.Collections.Generic;

namespace PerformanceMonitorDashboard.Analysis;

/// <summary>
/// A scored observation from collected data.
/// </summary>
public class Fact
{
public string Source { get; set; } = string.Empty;
public string Key { get; set; } = string.Empty;
public double Value { get; set; }
public double BaseSeverity { get; set; }
public double Severity { get; set; }
public int ServerId { get; set; }
public string? DatabaseName { get; set; }

/// <summary>
/// Raw metric values for analysis and audit trail.
/// Keys are metric-specific (e.g., "wait_time_ms", "waiting_tasks_count").
/// </summary>
public Dictionary<string, double> Metadata { get; set; } = [];

/// <summary>
/// Amplifiers that were evaluated for this fact.
/// </summary>
public List<AmplifierResult> AmplifierResults { get; set; } = [];
}

/// <summary>
/// Result of evaluating a single amplifier against the fact set.
/// </summary>
public class AmplifierResult
{
public string Description { get; set; } = string.Empty;
public bool Matched { get; set; }
public double Boost { get; set; }
}

/// <summary>
/// A conditional edge in the relationship graph.
/// </summary>
public class Edge
{
public string Source { get; set; } = string.Empty;
public string Destination { get; set; } = string.Empty;
public string Category { get; set; } = string.Empty;
public string PredicateDescription { get; set; } = string.Empty;

/// <summary>
/// Evaluates whether this edge should be followed given the current fact set.
/// </summary>
public Func<IReadOnlyDictionary<string, Fact>, bool> Predicate { get; set; } = _ => false;
}

/// <summary>
/// A complete analysis story — the path from root symptom to leaf recommendation.
/// </summary>
public class AnalysisStory
{
public string RootFactKey { get; set; } = string.Empty;
public double RootFactValue { get; set; }
public double Severity { get; set; }
public double Confidence { get; set; }
public string Category { get; set; } = string.Empty;
public List<string> Path { get; set; } = [];
public string StoryPath { get; set; } = string.Empty;
public string StoryPathHash { get; set; } = string.Empty;
public string StoryText { get; set; } = string.Empty;
public string? LeafFactKey { get; set; }
public double? LeafFactValue { get; set; }
public int FactCount { get; set; }
public bool IsAbsolution { get; set; }
}

/// <summary>
/// A persisted finding from a previous analysis run.
/// Maps to the analysis_findings DuckDB table.
/// </summary>
public class AnalysisFinding
{
public long FindingId { get; set; }
public DateTime AnalysisTime { get; set; }
public int ServerId { get; set; }
public string ServerName { get; set; } = string.Empty;
public string? DatabaseName { get; set; }
public DateTime? TimeRangeStart { get; set; }
public DateTime? TimeRangeEnd { get; set; }
public double Severity { get; set; }
public double Confidence { get; set; }
public string Category { get; set; } = string.Empty;
public string StoryPath { get; set; } = string.Empty;
public string StoryPathHash { get; set; } = string.Empty;
public string StoryText { get; set; } = string.Empty;
public string RootFactKey { get; set; } = string.Empty;
public double? RootFactValue { get; set; }
public string? LeafFactKey { get; set; }
public double? LeafFactValue { get; set; }
public int FactCount { get; set; }

/// <summary>
/// Drill-down data collected after graph traversal. Ephemeral — not persisted to DuckDB.
/// Contains supporting detail keyed by category (e.g., "top_deadlocks", "queries_at_spike").
/// </summary>
public Dictionary<string, object>? DrillDown { get; set; }
}

/// <summary>
/// A muted finding pattern. Maps to the analysis_muted DuckDB table.
/// </summary>
public class AnalysisMuted
{
public long MuteId { get; set; }
public int? ServerId { get; set; }
public string? DatabaseName { get; set; }
public string StoryPathHash { get; set; } = string.Empty;
public string StoryPath { get; set; } = string.Empty;
public DateTime MutedDate { get; set; }
public string? Reason { get; set; }
}

/// <summary>
/// A user-configured exclusion filter. Maps to the analysis_exclusions DuckDB table.
/// </summary>
public class AnalysisExclusion
{
public long ExclusionId { get; set; }
public string ExclusionType { get; set; } = string.Empty;
public string ExclusionValue { get; set; } = string.Empty;
public int? ServerId { get; set; }
public string? DatabaseName { get; set; }
public bool IsEnabled { get; set; } = true;
public DateTime CreatedDate { get; set; }
public string? Description { get; set; }
}

/// <summary>
/// A severity threshold value. Maps to the analysis_thresholds DuckDB table.
/// </summary>
public class AnalysisThreshold
{
public long ThresholdId { get; set; }
public string Category { get; set; } = string.Empty;
public string FactKey { get; set; } = string.Empty;
public string ThresholdType { get; set; } = string.Empty;
public double ThresholdValue { get; set; }
public int? ServerId { get; set; }
public string? DatabaseName { get; set; }
public bool IsEnabled { get; set; } = true;
public DateTime ModifiedDate { get; set; }
}
Loading
Loading