Skip to content

Plan analysis: per-rule disable and severity overrides from darling.json and Lite's settings (#4535) - #4602

Merged
erikdarlingdata merged 26 commits into
devfrom
plan-sync/4535-config-loading
Sep 28, 2026
Merged

erikdarlingdata merged 26 commits into
devfrom
plan-sync/4535-config-loading

Conversation

@erikdarlingdata

@erikdarlingdata erikdarlingdata commented Sep 28, 2026 •

Copy link
Copy Markdown
Owner

Fixes #4535. #4585 is merged; this is now against dev. Part of #4511.

What changes

The per-rule analyzer configuration ported in #4585 (AnalyzerConfig, PlanAnalyzer's disable/severity-override checks) now actually loads from each host's own settings file, so the config takes effect instead of always defaulting.

  • PerformanceMonitor.PlanAnalysis/ConfigLoader.cs: Parse(json) and LoadFile(path), ported from erikdarlingdata/PerformanceStudio's ConfigLoader. A malformed or missing file/section returns AnalyzerConfig.Default rather than throwing. No auto-discovery of ~/.planview.json — each host resolves its own file path and hands the section text to Parse.
  • Darling (the service): an optional "analyzer": { "rules": { "disabled": [...], "severity_overrides": {...} } } section in darling.json, bound onto DarlingConfig.Analyzer. Threaded into both drill-down collectors, the MCP plan tools (DarlingMcpPlanTools, via a DI-registered AnalyzerConfig singleton — see the MCP-schema note below), and McpPlanAnalysisFormatter.BuildAnalysisResult's new config-aware overload. Also threaded into the MCP host's own DarlingAnalysisService construction (DarlingMcpHostService.cs) — that call was still passing no analyzer config, so get_analysis_facts/analyze_server/drill-down plan advisories over MCP silently used AnalyzerConfig.Default while the scheduled worker pass and the web endpoints already honored a user's overrides on the same install. Now passes the same config.Analyzer ?? AnalyzerConfig.Default instance the host already resolved for its DI registration.
  • The Darling Viewer: reads the same section from darling.json (ViewerSettings.AnalyzerConfig) and sets PlanViewerControl.AnalyzerConfig, a new property LoadPlan passes into PlanAnalysisPipeline.Run.
  • Lite: reads an "analyzer" key from settings.json (App.AnalyzerConfig) with the same JSON shape, and passes it to Lite's drill-down collector and MCP plan tools the same way.
  • Docs: a commented example added to Lite/config/settings.sample.json and Darling/PerformanceMonitor.Darling.Service/darling.sample.json — "analyzer": { "rules": { "disabled": [20], "severity_overrides": { "5": "Low" } } } hides the Local Variables finding (rule 20) and downgrades rule 5's severity to Low.

MCP schema check

DarlingMcpPlanTools' four tools take AnalyzerConfig? analyzerConfig = null as a method parameter, resolved from DI the same way NpgsqlDataSource already is (no [Description] attribute). DarlingMcpPlanToolsSurfaceAndSqlTests.AdvertisedSchema_* builds the actual tools/list schema through the same WithGeminiCompatibleTools path Program wiring uses and asserts the required/optional parameter sets — analyzerConfig does not appear in any tool's schema, confirmed by those tests passing. Lite's tools read a static App.AnalyzerConfig directly rather than taking it as a parameter, so there's no schema exposure there either.

Fixes found while wiring this up

  • DarlingMcpHostService.cs registered AnalyzerConfig via the untyped AddSingleton(instance) overload, which McpServiceParameterDiSeatCensusTests requires to be the typed-generic AddSingleton<T> form (the same census Store host visibility: a cross-platform host profile, a --check-settings verb, and a get_store_host read, so "is this store sized right" has an answer without shell access #4214's round-1 review added for PostgresConfig/StoreHostProfileCache). Switched to AddSingleton<AnalyzerConfig> and added AnalyzerConfig to the census's pinned five-type list (now six).
  • Lite/App.xaml.cs inserted the new AnalyzerConfig property between an existing /// <summary> doc comment and its member (DefaultTimeRangeHours), stacking two doc blocks on one member — the classic failure the plan-sync guidance calls out. Reordered so each property keeps its own doc comment.
  • The MCP host's own analysis-service construction (DarlingMcpHostService.cs:526) passed no analyzer config, so MCP's plan advisories disagreed with the worker and web endpoints on the same install. Fixed by passing config.Analyzer ?? AnalyzerConfig.Default, same as the DI registration two lines above it.
  • Checked Lite for the same gap: every plan-analysis call site there (DrillDownCollector.Plans.cs, DuckDbFactCollector.QueryPerf.cs) reads the static App.AnalyzerConfig directly rather than taking a constructor/DI parameter, so AnalysisService's constructor sites (RecommendationsTab.xaml.cs:232, Mcp/McpHostService.cs:73, Services/CollectionBackgroundService.cs:663) have nothing to thread — Lite was never affected by this gap.

Test plan

Built Release with -p:EnableWindowsTargeting=true: Darling.Tests, Lite/PerformanceMonitorLite.csproj, Lite.Tests, PerformanceMonitor.Ui, Darling/PerformanceMonitor.Darling.Service, the Darling Viewer, deprecated/Dashboard.Tests — all 0 warnings / 0 errors.

Ran in-process on macOS (Darling.Tests.dll, Microsoft.WindowsDesktop.App stripped from the runtimeconfig): the new PlanSync4535ConfigLoadingTests (12 pins: ConfigLoader.Parse/LoadFile round-trip and fallback, the darling.json "analyzer" section binding, the end-to-end MCP formatter path with a disabled rule and a severity override, the viewer's helper seam reaching PlanAnalysisPipeline.Run), every other PlanSync4535* class, BenefitScorerWiringTests, the plan-sync consumer classes, the census family (CommentFilterAdoptionTests, DocCommentHygieneTests, McpPayloadContractCensusTests, McpToolsListBudgetTests, every *Census* class) — 604 total, 0 failed, 3 skipped (live-Postgres gated, expected without DARLING_TEST_PG).

RED confirmed against 6fb64ded2 (the plan-sync/4535-analyzer-config head, in a detached worktree): the new test file fails to compile there — ConfigLoader doesn't exist, DarlingConfig.Analyzer doesn't exist, and BuildAnalysisResult has no 6-argument overload. Compile-only RED, labelled.

The MCP host fix added PlanSync4602AnalysisServiceConfigCensusTests, a source census that asserts every production new DarlingAnalysisService( call carries an analyzer-config argument. Run against 4dc30e743 (this branch's own prior head, in a detached worktree) it failed at runtime with one message: Darling/PerformanceMonitor.Darling.Service/Mcp/DarlingMcpHostService.cs: 'new DarlingAnalysisService(postgres, planFetcher, _logger, _baselineCache)' has no analyzer-config argument — MCP's #4602 gap. GREEN after the fix, along with the updated SharedBaselineCacheTests pin on the same construction line. Re-ran the full shared plan-sync test list plus the census family after merging dev (c650606fe) into the branch: 350 total, 0 failed, 1 skipped (live-Postgres gated). Darling.Tests/Lite.Tests builds both 0 warnings/0 errors.

Plus one runtime mutation: forced PlanAnalyzer.TryOverrideSeverity's overrideSeverity local to always be null, ran BuildAnalysisResult_SeverityOverride_ChangesReportedSeverity — failed (Expected: "Info", Actual: "Warning") — then reverted; the class is back to its committed content (confirmed via git diff, no residual change).

Lite.Tests builds (0 errors) but does not run on macOS (discovery dies on WindowsBase); CI decides it. No live-Postgres pins were added by this branch beyond the three already-gated classes in the shared test list.

CHANGELOG

SECTION: Added
ENTRY: - Per-rule plan analyzer overrides ([#4602]) - darling.json's (Darling) and settings.json's (Lite) new optional analyzer section can disable a specific plan-analysis rule or override its severity, e.g. "analyzer": { "rules": { "disabled": [20] } } hides Local Variables findings. Applies to the MCP plan tools, the drill-down views, and the plan viewer window.
REF: [#4602]: #4602

erikdarlingdata and others added 24 commits September 28, 2026 09:15
Ports PerformanceStudio's AnalyzerConfig/RulesConfig (disabled rules,
severity overrides) and the top-level ServerMetadata fields into
PerformanceMonitor.PlanAnalysis, adds PlanWarning.RuleNumber, and adds
Analyze/Run overloads that take (config, serverMetadata, cancellationToken)
and thread both down to the statement and node walks. The existing
overloads still forward to AnalyzerConfig.Default and a null metadata, and
no rule reads either new parameter yet, so analyzer output is unchanged.
…ing (#4535)

Ports erikdarlingdata/PerformanceStudio's severity-override pass: overrides now find a warning's rule via PlanWarning.RuleNumber instead of matching WarningType against a rule-to-name table, and the engine's own warnings (Source == SqlServer) are always skipped. Also ports PlanStatements.EnumerateAllWithContainer, with EnumerateAll delegating to it so the plain and container-carrying walks stay one walk.
#4535, #4566)

The plumbing step's RuleNumber_DefaultsToNull_UntilLaterStepsStampIt pinned the
state partway through the per-rule configuration rollout (only rule 3 stamped).
Every step that stamps a rule has landed on this branch, so that pin is wrong
by design now.

Replaces it with PlanSync4535RuleNumberCensusTests: a code census over every
new PlanWarning construction in PlanAnalyzer.cs (each must set RuleNumber,
except a SqlServer-sourced initializer), a runtime census over the existing
plan fixtures (every analyzer finding carries a non-null RuleNumber), and a
cross-step check that disabling every stamped rule number leaves zero analyzer
findings.
…ins, and fix the Lite sample's trailing comma
The MCP host built its DarlingAnalysisService with no analyzerConfig
argument, so get_analysis_facts, analyze_server and drill-down plan
advisories silently fell back to AnalyzerConfig.Default while the
scheduled worker pass and the web endpoints already honored a user's
disabled or overridden rules from darling.json. Same fix as those two:
pass the config instance the host already resolved for its own DI
registration.

A source census pins that every production construction site of
DarlingAnalysisService carries an analyzer-config argument; it is RED
against the pre-fix code at the MCP host's line and GREEN here.
@erikdarlingdata
erikdarlingdata marked this pull request as ready for review September 28, 2026 17:59
@erikdarlingdata
erikdarlingdata merged commit 35e3bfc into dev Sep 28, 2026
15 of 16 checks passed
@erikdarlingdata
erikdarlingdata deleted the plan-sync/4535-config-loading branch September 28, 2026 17:59
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