Summary
Adopt Central Package Management (CPM) — a single Directory.Packages.props
that owns every dependency version — codify a dependency-version policy that
keeps redistributable-library floors low, and exclude the deliberately-floored
packages from the outdated-package CI check so they stop generating recurring
"NuGet Package Issues Detected" issues.
Motivation
Each of the ~22 .csproj files carries its own Version="…" per PackageReference:
-
Duplication / drift risk. The test stack (NUnit 4.6.1, NUnit3TestAdapter 6.2.0,
NUnit.Console 3.22.0, Microsoft.NET.Test.Sdk 18.6.0, coverlet.* 10.0.1,
Microsoft.Extensions.Logging.Console 10.0.9) is repeated in every test project.
-
Library floors are too high. The redistributable libraries multi-target
netstandard2.1;net10.0 and pin runtime-era versions that become a minimum
floor for every downstream consumer (the floor bites hardest on the
netstandard2.1 target; on net10.0 these are in-box/unified). These abstraction
packages have been API-stable since 6.0 and NuGet unifies to the consuming app's
version anyway, so the high floor only narrows compatibility.
-
The outdated-check fights the policy. .github/workflows/nuget-reference-check.yml
runs dotnet list package --outdated daily and opens/updates a "NuGet Package
Issues Detected" issue. Once we intentionally floor a package, it is by design
"outdated" forever, so the check would nag continuously unless excluded.
Packages to floor (exhaustive, from current dependencies)
Only stable framework-abstraction packages referenced by redistributable
libraries are floored. Suggested floor: latest LTS (e.g. 8.0.x); final
version at implementer discretion, provided the APIs used still resolve.
| Package |
Referenced by (library) |
Current |
Action |
Microsoft.Extensions.Logging.Abstractions |
SysML2.NET, SysML2.NET.Serializer.TextualNotation |
10.0.9 |
Floor to LTS |
System.Collections.Immutable |
SysML2.NET, SysML2.NET.Serializer.TextualNotation |
10.0.9 |
Floor to LTS |
System.IO.Pipelines |
SysML2.NET.Serializer.MessagePack |
10.0.9 |
Floor to LTS (note: MessagePack's own transitive floor wins if higher) |
Proposed approach
- Add
Directory.Packages.props at the repo root (<ManagePackageVersionsCentrally>true</…>, one <PackageVersion> per package).
- Strip
Version="…" from every <PackageReference>.
- Floor the three packages in the table above to an LTS version.
- Extend
IGNORE_PACKAGES in .github/workflows/nuget-reference-check.yml with those three floored packages so they no longer trigger the outdated issue — while remaining covered by the --deprecated and --vulnerable steps (which do not use the ignore filter).
Dependency-version policy
- Redistributable libraries (
SysML2.NET, .Serializer.*, .Dal, .REST,
.Kpar, .Extensions, .PIM): reference the lowest version providing the APIs
used. Keep stable framework abstractions (Microsoft.Extensions.*,
System.Collections.Immutable, System.IO.Pipelines, …) at an LTS floor; do
not chase majors.
- Applications & test projects (
SysML2.NET.Viewer, all *.Tests): keep current.
- Bump a library dependency only when (a) a required new API needs it, or (b) a
CVE affects that specific package. Serialization/crypto packages track current.
CI: exclude floored packages from the outdated check (not from security)
- Add the three floored packages to
IGNORE_PACKAGES in nuget-reference-check.yml
(the regex on line ~33). This suppresses only the outdated signal.
- Do NOT exclude them from
--deprecated or --vulnerable — a floored package
with a CVE must still surface.
- Keep the floored/ignored list as a single source of truth: list the floored
packages once (e.g. a comment block in Directory.Packages.props) and mirror
them in the workflow's IGNORE_PACKAGES, so the two never drift.
Scope / tasks
Out of scope
- Mass-bumping application/test package versions.
- Changing target frameworks.
- Weakening the deprecated/vulnerable checks (those stay strict on every package).
References
Summary
Adopt Central Package Management (CPM) — a single
Directory.Packages.propsthat owns every dependency version — codify a dependency-version policy that
keeps redistributable-library floors low, and exclude the deliberately-floored
packages from the outdated-package CI check so they stop generating recurring
"NuGet Package Issues Detected" issues.
Motivation
Each of the ~22
.csprojfiles carries its ownVersion="…"perPackageReference:Duplication / drift risk. The test stack (
NUnit 4.6.1,NUnit3TestAdapter 6.2.0,NUnit.Console 3.22.0,Microsoft.NET.Test.Sdk 18.6.0,coverlet.* 10.0.1,Microsoft.Extensions.Logging.Console 10.0.9) is repeated in every test project.Library floors are too high. The redistributable libraries multi-target
netstandard2.1;net10.0and pin runtime-era versions that become a minimumfloor for every downstream consumer (the floor bites hardest on the
netstandard2.1target; onnet10.0these are in-box/unified). These abstractionpackages have been API-stable since 6.0 and NuGet unifies to the consuming app's
version anyway, so the high floor only narrows compatibility.
The outdated-check fights the policy.
.github/workflows/nuget-reference-check.ymlruns
dotnet list package --outdateddaily and opens/updates a "NuGet PackageIssues Detected" issue. Once we intentionally floor a package, it is by design
"outdated" forever, so the check would nag continuously unless excluded.
Packages to floor (exhaustive, from current dependencies)
Only stable framework-abstraction packages referenced by redistributable
libraries are floored. Suggested floor: latest LTS (e.g.
8.0.x); finalversion at implementer discretion, provided the APIs used still resolve.
Microsoft.Extensions.Logging.AbstractionsSysML2.NET,SysML2.NET.Serializer.TextualNotationSystem.Collections.ImmutableSysML2.NET,SysML2.NET.Serializer.TextualNotationSystem.IO.PipelinesSysML2.NET.Serializer.MessagePackMessagePack's own transitive floor wins if higher)Proposed approach
Directory.Packages.propsat the repo root (<ManagePackageVersionsCentrally>true</…>, one<PackageVersion>per package).Version="…"from every<PackageReference>.IGNORE_PACKAGESin.github/workflows/nuget-reference-check.ymlwith those three floored packages so they no longer trigger the outdated issue — while remaining covered by the--deprecatedand--vulnerablesteps (which do not use the ignore filter).Dependency-version policy
SysML2.NET,.Serializer.*,.Dal,.REST,.Kpar,.Extensions,.PIM): reference the lowest version providing the APIsused. Keep stable framework abstractions (
Microsoft.Extensions.*,System.Collections.Immutable,System.IO.Pipelines, …) at an LTS floor; donot chase majors.
SysML2.NET.Viewer, all*.Tests): keep current.CVE affects that specific package. Serialization/crypto packages track current.
CI: exclude floored packages from the outdated check (not from security)
IGNORE_PACKAGESinnuget-reference-check.yml(the regex on line ~33). This suppresses only the outdated signal.
--deprecatedor--vulnerable— a floored packagewith a CVE must still surface.
packages once (e.g. a comment block in
Directory.Packages.props) and mirrorthem in the workflow's
IGNORE_PACKAGES, so the two never drift.Scope / tasks
Directory.Packages.propscentralizing all versions.Version=from everyPackageReference.Microsoft.Extensions.Logging.Abstractions,System.Collections.Immutable, andSystem.IO.Pipelinesto an LTS version (per the table).IGNORE_PACKAGESin.github/workflows/nuget-reference-check.ymlwith those three packages (outdated check only).README.md,CLAUDE.md, and the project wiki.dotnet restore+dotnet build+ fulldotnet testgreen;CodeQuality.ymlunaffected;nuget-reference-check.ymlproduces no recurring outdated issue for floored packages.Out of scope
References
dotnet list package --outdated: https://learn.microsoft.com/dotnet/core/tools/dotnet-list-package