diff --git a/.editorconfig b/.editorconfig
index 850846d0b..c6c807297 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -106,13 +106,17 @@ ij_json_wrap_long_lines = false
dotnet_diagnostic.ca1848.severity = none
# --- Static-analysis pack adoption (ersatztv#15) ---
-# Roslynator / SonarAnalyzer / Meziantou / AsyncFixer are referenced centrally
-# (Directory.Build.targets). Default every analyzer diagnostic to `suggestion` so the new
-# packs don't fail the TreatWarningsAsErrors build; high-value rules get promoted to
-# warning/error one at a time (see ersatztv#15 / docs/contributing.md). Explicit per-rule
-# severities (e.g. ca1848 above) still take precedence over this bulk default.
+# Threading analyzers and Roslynator / SonarAnalyzer / Meziantou / AsyncFixer are enabled centrally.
+# Default their diagnostics to `suggestion`; the SDK's exact per-rule suggestion baseline lives in
+# eng/analyzers/sdk-all-suggestion.globalconfig because AnalysisLevel=latest-All otherwise injects
+# exact warning severities that outrank this bulk setting. High-value rules are promoted one at a
+# time. Explicit per-rule severities (e.g. ca1848 above) take precedence over both baselines.
dotnet_analyzer_diagnostic.severity = suggestion
+# A collection count can never be negative. Treat comparisons that therefore collapse to a
+# constant as errors; the first promotion caught a busy/idle branch that was permanently busy.
+dotnet_diagnostic.S3981.severity = warning
+
# Blazor components: analyzers run on .razor/.cshtml @code too, and TWAE would otherwise
# turn their default-severity findings into build errors — keep them at suggestion as well.
[*.razor]
diff --git a/Directory.Build.props b/Directory.Build.props
index 74f7120cb..d65409840 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -3,6 +3,12 @@
develop
false
true
+
+ true
+ latest-All
+ true
+ TreatWarningsAsErrors), so "criticals block" actually holds repo-wide.
+ S3981 is the first explicitly promoted analyzer rule (ersatztv#15). -->
$(WarningsNotAsErrors);NU1901;NU1902;NU1903
- $(WarningsAsErrors);NU1904
+ $(WarningsAsErrors);NU1904;S3981
+
+
+
+
diff --git a/Directory.Build.targets b/Directory.Build.targets
index 8703b853f..030a436ef 100644
--- a/Directory.Build.targets
+++ b/Directory.Build.targets
@@ -1,9 +1,7 @@
-
- false
-
-
-
+
+
@@ -12,11 +10,11 @@
-
+
all
diff --git a/ErsatzTV.Application/ErsatzTV.Application.csproj b/ErsatzTV.Application/ErsatzTV.Application.csproj
index 73d35a499..4625ba925 100644
--- a/ErsatzTV.Application/ErsatzTV.Application.csproj
+++ b/ErsatzTV.Application/ErsatzTV.Application.csproj
@@ -4,7 +4,6 @@
net10.0
VSTHRD200,CA1873
enable
- latest-Recommended
true
Debug;Release;Debug No Sync
diff --git a/ErsatzTV.Application/Maintenance/Commands/ReleaseMemoryHandler.cs b/ErsatzTV.Application/Maintenance/Commands/ReleaseMemoryHandler.cs
index 279b056e6..bbdf6cb2d 100644
--- a/ErsatzTV.Application/Maintenance/Commands/ReleaseMemoryHandler.cs
+++ b/ErsatzTV.Application/Maintenance/Commands/ReleaseMemoryHandler.cs
@@ -27,7 +27,7 @@ public class ReleaseMemoryHandler : IRequestHandler
return Task.CompletedTask;
}
- bool hasActiveWorkers = _ffmpegSegmenterService.Workers.Count >= 0 || FFmpegProcess.ProcessCount > 0;
+ bool hasActiveWorkers = _ffmpegSegmenterService.Workers.Count > 0 || FFmpegProcess.ProcessCount > 0;
if (request.ForceAggressive || !hasActiveWorkers)
{
_logger.LogDebug("Starting aggressive garbage collection");
diff --git a/ErsatzTV.Core/ErsatzTV.Core.csproj b/ErsatzTV.Core/ErsatzTV.Core.csproj
index d89b45f03..b492f378d 100644
--- a/ErsatzTV.Core/ErsatzTV.Core.csproj
+++ b/ErsatzTV.Core/ErsatzTV.Core.csproj
@@ -4,7 +4,6 @@
net10.0
VSTHRD200,CA1873
enable
- latest-Recommended
true
disable
diff --git a/ErsatzTV.FFmpeg/ErsatzTV.FFmpeg.csproj b/ErsatzTV.FFmpeg/ErsatzTV.FFmpeg.csproj
index ae249a3d3..5b88292b9 100644
--- a/ErsatzTV.FFmpeg/ErsatzTV.FFmpeg.csproj
+++ b/ErsatzTV.FFmpeg/ErsatzTV.FFmpeg.csproj
@@ -4,7 +4,6 @@
net10.0
enable
enable
- latest-Recommended
true
true
VSTHRD200,CA1873
diff --git a/ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj b/ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj
index 1613bf6fd..9608cf1cc 100644
--- a/ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj
+++ b/ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj
@@ -5,7 +5,6 @@
true
VSTHRD200,CA1873
enable
- latest-Recommended
true
Linux
true
diff --git a/ErsatzTV.Scanner/ErsatzTV.Scanner.csproj b/ErsatzTV.Scanner/ErsatzTV.Scanner.csproj
index 677e3e8d8..b314b7327 100644
--- a/ErsatzTV.Scanner/ErsatzTV.Scanner.csproj
+++ b/ErsatzTV.Scanner/ErsatzTV.Scanner.csproj
@@ -8,7 +8,6 @@
Debug;Release;Debug No Sync
AnyCPU
729e6271-c307-43c8-8e36-1b36c39f6de2
- latest-Recommended
true
VSTHRD200,CA1873
diff --git a/ErsatzTV.Tests/Application/Maintenance/ReleaseMemoryHandlerTests.cs b/ErsatzTV.Tests/Application/Maintenance/ReleaseMemoryHandlerTests.cs
new file mode 100644
index 000000000..28d6fb363
--- /dev/null
+++ b/ErsatzTV.Tests/Application/Maintenance/ReleaseMemoryHandlerTests.cs
@@ -0,0 +1,49 @@
+using ErsatzTV.Application.Maintenance;
+using ErsatzTV.Core.FFmpeg;
+using ErsatzTV.Core.Interfaces.FFmpeg;
+using Microsoft.Extensions.Logging;
+using NSubstitute;
+using NUnit.Framework;
+using Shouldly;
+
+namespace ErsatzTV.Tests.Application.Maintenance;
+
+[TestFixture]
+[NonParallelizable]
+public class ReleaseMemoryHandlerTests
+{
+ [Test]
+ public async Task Should_Use_Aggressive_Collection_When_No_Workers_Are_Active()
+ {
+ FFmpegProcess.ProcessCount.ShouldBe(0);
+
+ IFFmpegSegmenterService segmenterService = Substitute.For();
+ segmenterService.Workers.Returns([]);
+ ILogger logger = Substitute.For>();
+ var handler = new ReleaseMemoryHandler(segmenterService, logger);
+
+ await handler.Handle(new ReleaseMemory(false), CancellationToken.None);
+
+ ShouldHaveLogged(logger, "Starting aggressive garbage collection");
+ }
+
+ [Test]
+ public async Task Should_Use_Regular_Collection_When_A_Worker_Is_Active()
+ {
+ FFmpegProcess.ProcessCount.ShouldBe(0);
+
+ IFFmpegSegmenterService segmenterService = Substitute.For();
+ segmenterService.Workers.Returns([Substitute.For()]);
+ ILogger logger = Substitute.For>();
+ var handler = new ReleaseMemoryHandler(segmenterService, logger);
+
+ await handler.Handle(new ReleaseMemory(false), CancellationToken.None);
+
+ ShouldHaveLogged(logger, "Starting garbage collection");
+ }
+
+ private static void ShouldHaveLogged(ILogger logger, string expectedMessage) =>
+ logger.ReceivedCalls()
+ .Any(call => call.GetArguments().ElementAtOrDefault(2)?.ToString() == expectedMessage)
+ .ShouldBeTrue();
+}
diff --git a/ErsatzTV/ErsatzTV.csproj b/ErsatzTV/ErsatzTV.csproj
index c94ffb3de..e3993490c 100644
--- a/ErsatzTV/ErsatzTV.csproj
+++ b/ErsatzTV/ErsatzTV.csproj
@@ -11,7 +11,6 @@
Debug;Release;Debug No Sync
AnyCPU
bf31217d-f4ec-4520-8cc3-138059044ede
- latest-Recommended
true
true
en-US
diff --git a/docker/Dockerfile b/docker/Dockerfile
index e3a989fb9..24e95ec85 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -45,6 +45,7 @@ COPY *.sln .
# local/CI builds. Directory.Packages.props is REQUIRED here: with CPM the csproj
# carry no versions, so restore fails without the central manifest.
COPY Directory.Build.props Directory.Build.targets Directory.Packages.props global.json .editorconfig ./
+COPY eng/analyzers/sdk-all-suggestion.globalconfig ./eng/analyzers/
COPY artwork/* ./artwork/
COPY ErsatzTV/*.csproj ./ErsatzTV/
COPY ErsatzTV.Application/*.csproj ./ErsatzTV.Application/
diff --git a/docs/ci-cd.md b/docs/ci-cd.md
index d47d87b64..fe37447dd 100644
--- a/docs/ci-cd.md
+++ b/docs/ci-cd.md
@@ -299,27 +299,43 @@ running product from outside our C#/review stack.
## Static analysis & formatting
-**Analyzer packs** — `Directory.Build.targets` references **Roslynator**, **SonarAnalyzer.CSharp**,
-**Meziantou.Analyzer**, and **AsyncFixer** for every project (versions central via CPM; guarded on
-`ManagePackageVersionsCentrally` so the gitignored `.mcp` tool isn't pulled in). They are introduced
-**incrementally** (ersatztv#15): `.editorconfig` sets `dotnet_analyzer_diagnostic.severity = suggestion`
-so the packs surface findings without failing the `TreatWarningsAsErrors` (TWAE) build. **Promotion is
-the enforcement** — raising a rule to `warning` makes it a CI-blocking error via the existing TWAE
-build, so no separate lint step is needed.
+**Analyzers** — `Directory.Build.props` enables the SDK analyzers at `latest-All` and turns on
+`Microsoft.VisualStudio.Threading.Analyzers` for every centrally managed project.
+`Directory.Build.targets` also references
+**Roslynator**, **SonarAnalyzer.CSharp**, **Meziantou.Analyzer**, and **AsyncFixer** repo-wide (versions
+central via CPM). All analyzer package references are guarded on `ManagePackageVersionsCentrally`, so the
+gitignored `.mcp` tool—which deliberately uses inline package versions—does not inherit versionless
+references.
+They are introduced **incrementally** (ersatztv#15). `eng/analyzers/sdk-all-suggestion.globalconfig`
+enumerates the .NET 10 SDK `All` inventory at `suggestion`; this exact-ID baseline is necessary because
+the SDK's generated `latest-All` severities outrank `.editorconfig` bulk settings. `.editorconfig` keeps
+the threading and curated-pack baselines at `suggestion`. Diagnostics remain visible to IDEs and
+`dotnet format analyzers`, but do not create a wall of failures (a direct `latest-All` trial activated
+455 existing errors in the TWAE projects).
+
+**Promotion is the enforcement** — set a reviewed rule to `warning` in `.editorconfig` and append its ID
+to the central `WarningsAsErrors` list in `Directory.Build.props`. The explicit list makes the rule block
+in every project, including test projects that do not otherwise use TWAE. On a major SDK upgrade,
+regenerate the checked-in SDK baseline from `analysislevel__all.globalconfig`, preserve SDK `none`
+entries, and review newly introduced rules before accepting the snapshot.
+
+Promoted rules are recorded here so the blocking subset stays intentional and reviewable:
+- **Sonar `S3981` — `warning` + `WarningsAsErrors`** (ersatztv#15): rejects collection-count comparisons that are constant
+ regardless of collection size. Its first finding exposed `Workers.Count >= 0`, which permanently
+ classified scheduled memory releases as busy and skipped the intended aggressive idle collection.
- **StyleCop.Analyzers is intentionally excluded**: its latest stable (1.1.118) crashes (`AD0001`) on
C# `record` declarations, and its rules overlap the existing `.editorconfig`/Roslynator. Revisit via
the record-compatible `1.2.0-beta` only if specifically wanted.
-- **Blazor `.razor` caveat**: editorconfig severity overrides don't reach analyzer diagnostics in Razor
- `@code` (source-generator limitation — `dotnet format` can't fix them either), so the currently-firing
- SonarAnalyzer rules are temporarily `NoWarn`-ed in `ErsatzTV.csproj` and burned down rule-by-rule in
- **ersatztv#25**. The same rules run at `suggestion` on `.cs`.
+- **The former Blazor `.razor` caveat is retired**: Blazor removal deleted the Razor sources and their
+ temporary Sonar `NoWarn` list. The `.razor`/`.cshtml` suggestion scopes remain in `.editorconfig` only
+ as a defensive default if server-rendered view code is ever reintroduced.
-**Formatting** — the tree isn't yet `dotnet format`-clean (mixed UTF-8 BOM + whitespace inherited from
-upstream: ~1,500 BOM files + ~480 whitespace). A one-time normalization lands as its **own dedicated
-PR** (kept out of the analyzer work to stay reviewable); afterwards `dotnet format whitespace
---verify-no-changes` (+ `style`) joins the `test` job so drift can't return. `.gitattributes` already
-pins line endings.
+**Formatting** — the inherited tree still contains legacy UTF-8 BOM/whitespace debt, so the standing
+policy is **format as you touch**, not a mass rewrite (ersatztv#311). The Husky pre-commit hook runs
+`dotnet format --verify-no-changes` for staged C# files, and the blocking `format` CI job repeats that
+check for C# files changed by the PR. Untouched legacy files remain outside the gate; `.gitattributes`
+pins line endings. A one-time full-tree normalization remains a separate, unmade decision.
## Migration integrity (EF Core, both providers)
diff --git a/docs/contributing.md b/docs/contributing.md
index c4ddf6110..544444427 100644
--- a/docs/contributing.md
+++ b/docs/contributing.md
@@ -111,9 +111,13 @@ talks exclusively to the REST API. Every former Blazor route now 302-redirects t
- **`TreatWarningsAsErrors=true`** in the app projects — a warning fails the build. `NoWarn` carries a
small, documented exemption list (e.g. `VSTHRD200`, `CA1873`); NuGet-audit `NU1901-1903` are demoted
to warnings in `Directory.Build.props` while `NU1904` (critical) blocks.
-- **Static-analysis packs** (Roslynator, SonarAnalyzer, Meziantou, AsyncFixer) run at `suggestion` and
- are promoted to `warning`/`error` rule-by-rule; promotion is the enforcement (the TWAE build). New
- rules start at suggestion — never flip a wall of rules to error at once. (ersatztv#15)
+- **Static analysis is centralized**: `Directory.Build.props` enables the SDK analyzers at
+ `latest-All` plus the threading analyzer for every centrally managed project; `Directory.Build.targets`
+ adds Roslynator, SonarAnalyzer, Meziantou, and AsyncFixer. Analyzer package references are CPM-guarded
+ so the gitignored `.mcp` tool retains its inline-version dependency model. The SDK globalconfig and
+ `.editorconfig` keep the broad baseline at `suggestion`. Promote one reviewed rule at a time by setting
+ it to `warning` and adding its ID to the central `WarningsAsErrors` list; never flip a wall of rules to error at once.
+ Refresh the SDK globalconfig deliberately when moving to a new .NET SDK major. (ersatztv#15)
## 8. Testing
diff --git a/eng/analyzers/sdk-all-suggestion.globalconfig b/eng/analyzers/sdk-all-suggestion.globalconfig
new file mode 100644
index 000000000..47eac0629
--- /dev/null
+++ b/eng/analyzers/sdk-all-suggestion.globalconfig
@@ -0,0 +1,292 @@
+# .NET 10 SDK analyzer baseline for ersatztv#15.
+#
+# AnalysisLevel=latest-All enables the complete SDK rule inventory, whose generated
+# per-rule severities otherwise override .editorconfig's bulk suggestion setting.
+# This higher-priority snapshot keeps every current .NET 10 rule visible but non-blocking.
+# Preserve SDK 'none' entries for rules introduced after .NET 10. On an SDK major upgrade,
+# regenerate this list from analysislevel__all.globalconfig and review new rules.
+# A per-rule severity in the repository .editorconfig overrides this baseline.
+is_global = true
+global_level = -50
+
+dotnet_diagnostic.CA1000.severity = suggestion
+dotnet_diagnostic.CA1001.severity = suggestion
+dotnet_diagnostic.CA1002.severity = suggestion
+dotnet_diagnostic.CA1003.severity = suggestion
+dotnet_diagnostic.CA1008.severity = suggestion
+dotnet_diagnostic.CA1010.severity = suggestion
+dotnet_diagnostic.CA1012.severity = suggestion
+dotnet_diagnostic.CA1016.severity = suggestion
+dotnet_diagnostic.CA1018.severity = suggestion
+dotnet_diagnostic.CA1019.severity = suggestion
+dotnet_diagnostic.CA1024.severity = suggestion
+dotnet_diagnostic.CA1027.severity = suggestion
+dotnet_diagnostic.CA1028.severity = suggestion
+dotnet_diagnostic.CA1030.severity = suggestion
+dotnet_diagnostic.CA1031.severity = suggestion
+dotnet_diagnostic.CA1032.severity = suggestion
+dotnet_diagnostic.CA1033.severity = suggestion
+dotnet_diagnostic.CA1034.severity = suggestion
+dotnet_diagnostic.CA1036.severity = suggestion
+dotnet_diagnostic.CA1040.severity = suggestion
+dotnet_diagnostic.CA1041.severity = suggestion
+dotnet_diagnostic.CA1043.severity = suggestion
+dotnet_diagnostic.CA1044.severity = suggestion
+dotnet_diagnostic.CA1046.severity = suggestion
+dotnet_diagnostic.CA1047.severity = suggestion
+dotnet_diagnostic.CA1050.severity = suggestion
+dotnet_diagnostic.CA1051.severity = suggestion
+dotnet_diagnostic.CA1052.severity = suggestion
+dotnet_diagnostic.CA1054.severity = suggestion
+dotnet_diagnostic.CA1055.severity = suggestion
+dotnet_diagnostic.CA1056.severity = suggestion
+dotnet_diagnostic.CA1058.severity = suggestion
+dotnet_diagnostic.CA1061.severity = suggestion
+dotnet_diagnostic.CA1062.severity = suggestion
+dotnet_diagnostic.CA1063.severity = suggestion
+dotnet_diagnostic.CA1064.severity = suggestion
+dotnet_diagnostic.CA1065.severity = suggestion
+dotnet_diagnostic.CA1066.severity = suggestion
+dotnet_diagnostic.CA1067.severity = suggestion
+dotnet_diagnostic.CA1068.severity = suggestion
+dotnet_diagnostic.CA1069.severity = suggestion
+dotnet_diagnostic.CA1070.severity = suggestion
+dotnet_diagnostic.CA1200.severity = suggestion
+dotnet_diagnostic.CA1303.severity = suggestion
+dotnet_diagnostic.CA1304.severity = suggestion
+dotnet_diagnostic.CA1305.severity = suggestion
+dotnet_diagnostic.CA1307.severity = suggestion
+dotnet_diagnostic.CA1308.severity = suggestion
+dotnet_diagnostic.CA1309.severity = suggestion
+dotnet_diagnostic.CA1310.severity = suggestion
+dotnet_diagnostic.CA1311.severity = suggestion
+dotnet_diagnostic.CA1401.severity = suggestion
+dotnet_diagnostic.CA1419.severity = suggestion
+dotnet_diagnostic.CA1421.severity = suggestion
+dotnet_diagnostic.CA1507.severity = suggestion
+dotnet_diagnostic.CA1508.severity = suggestion
+dotnet_diagnostic.CA1510.severity = suggestion
+dotnet_diagnostic.CA1511.severity = suggestion
+dotnet_diagnostic.CA1512.severity = suggestion
+dotnet_diagnostic.CA1513.severity = suggestion
+dotnet_diagnostic.CA1514.severity = suggestion
+dotnet_diagnostic.CA1515.severity = suggestion
+dotnet_diagnostic.CA1516.severity = none
+dotnet_diagnostic.CA1700.severity = suggestion
+dotnet_diagnostic.CA1707.severity = suggestion
+dotnet_diagnostic.CA1708.severity = suggestion
+dotnet_diagnostic.CA1710.severity = suggestion
+dotnet_diagnostic.CA1711.severity = suggestion
+dotnet_diagnostic.CA1712.severity = suggestion
+dotnet_diagnostic.CA1713.severity = suggestion
+dotnet_diagnostic.CA1715.severity = suggestion
+dotnet_diagnostic.CA1716.severity = suggestion
+dotnet_diagnostic.CA1720.severity = suggestion
+dotnet_diagnostic.CA1721.severity = suggestion
+dotnet_diagnostic.CA1724.severity = suggestion
+dotnet_diagnostic.CA1725.severity = suggestion
+dotnet_diagnostic.CA1727.severity = suggestion
+dotnet_diagnostic.CA1802.severity = suggestion
+dotnet_diagnostic.CA1805.severity = suggestion
+dotnet_diagnostic.CA1806.severity = suggestion
+dotnet_diagnostic.CA1810.severity = suggestion
+dotnet_diagnostic.CA1812.severity = suggestion
+dotnet_diagnostic.CA1813.severity = suggestion
+dotnet_diagnostic.CA1814.severity = suggestion
+dotnet_diagnostic.CA1815.severity = suggestion
+dotnet_diagnostic.CA1816.severity = suggestion
+dotnet_diagnostic.CA1819.severity = suggestion
+dotnet_diagnostic.CA1820.severity = suggestion
+dotnet_diagnostic.CA1821.severity = suggestion
+dotnet_diagnostic.CA1822.severity = suggestion
+dotnet_diagnostic.CA1823.severity = suggestion
+dotnet_diagnostic.CA1824.severity = suggestion
+dotnet_diagnostic.CA1825.severity = suggestion
+dotnet_diagnostic.CA1826.severity = suggestion
+dotnet_diagnostic.CA1827.severity = suggestion
+dotnet_diagnostic.CA1828.severity = suggestion
+dotnet_diagnostic.CA1829.severity = suggestion
+dotnet_diagnostic.CA1830.severity = suggestion
+dotnet_diagnostic.CA1832.severity = suggestion
+dotnet_diagnostic.CA1833.severity = suggestion
+dotnet_diagnostic.CA1834.severity = suggestion
+dotnet_diagnostic.CA1835.severity = suggestion
+dotnet_diagnostic.CA1836.severity = suggestion
+dotnet_diagnostic.CA1837.severity = suggestion
+dotnet_diagnostic.CA1838.severity = suggestion
+dotnet_diagnostic.CA1839.severity = suggestion
+dotnet_diagnostic.CA1840.severity = suggestion
+dotnet_diagnostic.CA1841.severity = suggestion
+dotnet_diagnostic.CA1842.severity = suggestion
+dotnet_diagnostic.CA1843.severity = suggestion
+dotnet_diagnostic.CA1844.severity = suggestion
+dotnet_diagnostic.CA1845.severity = suggestion
+dotnet_diagnostic.CA1846.severity = suggestion
+dotnet_diagnostic.CA1847.severity = suggestion
+dotnet_diagnostic.CA1848.severity = suggestion
+dotnet_diagnostic.CA1849.severity = suggestion
+dotnet_diagnostic.CA1850.severity = suggestion
+dotnet_diagnostic.CA1851.severity = suggestion
+dotnet_diagnostic.CA1852.severity = suggestion
+dotnet_diagnostic.CA1853.severity = suggestion
+dotnet_diagnostic.CA1854.severity = suggestion
+dotnet_diagnostic.CA1855.severity = suggestion
+dotnet_diagnostic.CA1856.severity = suggestion
+dotnet_diagnostic.CA1858.severity = suggestion
+dotnet_diagnostic.CA1859.severity = suggestion
+dotnet_diagnostic.CA1860.severity = suggestion
+dotnet_diagnostic.CA1861.severity = suggestion
+dotnet_diagnostic.CA1862.severity = suggestion
+dotnet_diagnostic.CA1863.severity = suggestion
+dotnet_diagnostic.CA1864.severity = suggestion
+dotnet_diagnostic.CA1865.severity = suggestion
+dotnet_diagnostic.CA1866.severity = suggestion
+dotnet_diagnostic.CA1867.severity = suggestion
+dotnet_diagnostic.CA1868.severity = suggestion
+dotnet_diagnostic.CA1869.severity = suggestion
+dotnet_diagnostic.CA1870.severity = suggestion
+dotnet_diagnostic.CA1871.severity = suggestion
+dotnet_diagnostic.CA1872.severity = suggestion
+dotnet_diagnostic.CA1873.severity = suggestion
+dotnet_diagnostic.CA1874.severity = suggestion
+dotnet_diagnostic.CA1875.severity = suggestion
+dotnet_diagnostic.CA2000.severity = suggestion
+dotnet_diagnostic.CA2002.severity = suggestion
+dotnet_diagnostic.CA2007.severity = suggestion
+dotnet_diagnostic.CA2008.severity = suggestion
+dotnet_diagnostic.CA2009.severity = suggestion
+dotnet_diagnostic.CA2011.severity = suggestion
+dotnet_diagnostic.CA2012.severity = suggestion
+dotnet_diagnostic.CA2016.severity = suggestion
+dotnet_diagnostic.CA2019.severity = suggestion
+dotnet_diagnostic.CA2020.severity = suggestion
+dotnet_diagnostic.CA2025.severity = suggestion
+dotnet_diagnostic.CA2100.severity = suggestion
+dotnet_diagnostic.CA2101.severity = suggestion
+dotnet_diagnostic.CA2119.severity = suggestion
+dotnet_diagnostic.CA2153.severity = suggestion
+dotnet_diagnostic.CA2201.severity = suggestion
+dotnet_diagnostic.CA2207.severity = suggestion
+dotnet_diagnostic.CA2208.severity = suggestion
+dotnet_diagnostic.CA2211.severity = suggestion
+dotnet_diagnostic.CA2213.severity = suggestion
+dotnet_diagnostic.CA2214.severity = suggestion
+dotnet_diagnostic.CA2215.severity = suggestion
+dotnet_diagnostic.CA2216.severity = suggestion
+dotnet_diagnostic.CA2217.severity = suggestion
+dotnet_diagnostic.CA2218.severity = suggestion
+dotnet_diagnostic.CA2219.severity = suggestion
+dotnet_diagnostic.CA2224.severity = suggestion
+dotnet_diagnostic.CA2225.severity = suggestion
+dotnet_diagnostic.CA2226.severity = suggestion
+dotnet_diagnostic.CA2227.severity = suggestion
+dotnet_diagnostic.CA2231.severity = suggestion
+dotnet_diagnostic.CA2234.severity = suggestion
+dotnet_diagnostic.CA2235.severity = suggestion
+dotnet_diagnostic.CA2237.severity = suggestion
+dotnet_diagnostic.CA2241.severity = suggestion
+dotnet_diagnostic.CA2242.severity = suggestion
+dotnet_diagnostic.CA2243.severity = suggestion
+dotnet_diagnostic.CA2244.severity = suggestion
+dotnet_diagnostic.CA2245.severity = suggestion
+dotnet_diagnostic.CA2246.severity = suggestion
+dotnet_diagnostic.CA2248.severity = suggestion
+dotnet_diagnostic.CA2249.severity = suggestion
+dotnet_diagnostic.CA2250.severity = suggestion
+dotnet_diagnostic.CA2251.severity = suggestion
+dotnet_diagnostic.CA2252.severity = suggestion
+dotnet_diagnostic.CA2253.severity = suggestion
+dotnet_diagnostic.CA2254.severity = suggestion
+dotnet_diagnostic.CA2262.severity = suggestion
+dotnet_diagnostic.CA2263.severity = suggestion
+dotnet_diagnostic.CA2300.severity = suggestion
+dotnet_diagnostic.CA2301.severity = suggestion
+dotnet_diagnostic.CA2302.severity = suggestion
+dotnet_diagnostic.CA2305.severity = suggestion
+dotnet_diagnostic.CA2310.severity = suggestion
+dotnet_diagnostic.CA2311.severity = suggestion
+dotnet_diagnostic.CA2312.severity = suggestion
+dotnet_diagnostic.CA2315.severity = suggestion
+dotnet_diagnostic.CA2321.severity = suggestion
+dotnet_diagnostic.CA2322.severity = suggestion
+dotnet_diagnostic.CA2326.severity = suggestion
+dotnet_diagnostic.CA2327.severity = suggestion
+dotnet_diagnostic.CA2328.severity = suggestion
+dotnet_diagnostic.CA2329.severity = suggestion
+dotnet_diagnostic.CA2330.severity = suggestion
+dotnet_diagnostic.CA2350.severity = suggestion
+dotnet_diagnostic.CA2351.severity = suggestion
+dotnet_diagnostic.CA2352.severity = suggestion
+dotnet_diagnostic.CA2353.severity = suggestion
+dotnet_diagnostic.CA2354.severity = suggestion
+dotnet_diagnostic.CA2355.severity = suggestion
+dotnet_diagnostic.CA2356.severity = suggestion
+dotnet_diagnostic.CA2361.severity = suggestion
+dotnet_diagnostic.CA2362.severity = suggestion
+dotnet_diagnostic.CA3001.severity = suggestion
+dotnet_diagnostic.CA3002.severity = suggestion
+dotnet_diagnostic.CA3003.severity = suggestion
+dotnet_diagnostic.CA3004.severity = suggestion
+dotnet_diagnostic.CA3005.severity = suggestion
+dotnet_diagnostic.CA3006.severity = suggestion
+dotnet_diagnostic.CA3007.severity = suggestion
+dotnet_diagnostic.CA3008.severity = suggestion
+dotnet_diagnostic.CA3009.severity = suggestion
+dotnet_diagnostic.CA3010.severity = suggestion
+dotnet_diagnostic.CA3011.severity = suggestion
+dotnet_diagnostic.CA3012.severity = suggestion
+dotnet_diagnostic.CA3061.severity = suggestion
+dotnet_diagnostic.CA3075.severity = suggestion
+dotnet_diagnostic.CA3076.severity = suggestion
+dotnet_diagnostic.CA3077.severity = suggestion
+dotnet_diagnostic.CA3147.severity = suggestion
+dotnet_diagnostic.CA5350.severity = suggestion
+dotnet_diagnostic.CA5351.severity = suggestion
+dotnet_diagnostic.CA5358.severity = suggestion
+dotnet_diagnostic.CA5359.severity = suggestion
+dotnet_diagnostic.CA5360.severity = suggestion
+dotnet_diagnostic.CA5361.severity = suggestion
+dotnet_diagnostic.CA5362.severity = suggestion
+dotnet_diagnostic.CA5363.severity = suggestion
+dotnet_diagnostic.CA5364.severity = suggestion
+dotnet_diagnostic.CA5365.severity = suggestion
+dotnet_diagnostic.CA5366.severity = suggestion
+dotnet_diagnostic.CA5367.severity = suggestion
+dotnet_diagnostic.CA5368.severity = suggestion
+dotnet_diagnostic.CA5369.severity = suggestion
+dotnet_diagnostic.CA5370.severity = suggestion
+dotnet_diagnostic.CA5371.severity = suggestion
+dotnet_diagnostic.CA5372.severity = suggestion
+dotnet_diagnostic.CA5373.severity = suggestion
+dotnet_diagnostic.CA5374.severity = suggestion
+dotnet_diagnostic.CA5375.severity = suggestion
+dotnet_diagnostic.CA5376.severity = suggestion
+dotnet_diagnostic.CA5377.severity = suggestion
+dotnet_diagnostic.CA5378.severity = suggestion
+dotnet_diagnostic.CA5379.severity = suggestion
+dotnet_diagnostic.CA5380.severity = suggestion
+dotnet_diagnostic.CA5381.severity = suggestion
+dotnet_diagnostic.CA5382.severity = suggestion
+dotnet_diagnostic.CA5383.severity = suggestion
+dotnet_diagnostic.CA5384.severity = suggestion
+dotnet_diagnostic.CA5385.severity = suggestion
+dotnet_diagnostic.CA5386.severity = suggestion
+dotnet_diagnostic.CA5387.severity = suggestion
+dotnet_diagnostic.CA5388.severity = suggestion
+dotnet_diagnostic.CA5389.severity = suggestion
+dotnet_diagnostic.CA5390.severity = suggestion
+dotnet_diagnostic.CA5391.severity = suggestion
+dotnet_diagnostic.CA5392.severity = suggestion
+dotnet_diagnostic.CA5393.severity = suggestion
+dotnet_diagnostic.CA5394.severity = suggestion
+dotnet_diagnostic.CA5395.severity = suggestion
+dotnet_diagnostic.CA5396.severity = suggestion
+dotnet_diagnostic.CA5397.severity = suggestion
+dotnet_diagnostic.CA5398.severity = suggestion
+dotnet_diagnostic.CA5399.severity = suggestion
+dotnet_diagnostic.CA5400.severity = suggestion
+dotnet_diagnostic.CA5401.severity = suggestion
+dotnet_diagnostic.CA5402.severity = suggestion
+dotnet_diagnostic.CA5403.severity = suggestion
+dotnet_diagnostic.CA5404.severity = suggestion
+dotnet_diagnostic.CA5405.severity = suggestion