Skip to content

Verify the netstandard2.0 libraries on the .NET Framework 4.7.2 floor - #198

Merged
Reefact merged 3 commits into
mainfrom
claude/dotnet-framework-47x-tests-qvx37f
Jul 19, 2026
Merged

Verify the netstandard2.0 libraries on the .NET Framework 4.7.2 floor#198
Reefact merged 3 commits into
mainfrom
claude/dotnet-framework-47x-tests-qvx37f

Conversation

@Reefact

@Reefact Reefact commented Jul 19, 2026

Copy link
Copy Markdown
Owner

Summary

The netstandard2.0 libraries were advertised as running on .NET Framework 4.6.1+, but that claim was never exercised in CI. This pins the honest, tested floor at 4.7.2 (the first .NET Framework that ships the netstandard2.0 facades in-box) and enforces it with a dedicated Windows CI job, so the support claim is verified on every pull request instead of merely asserted.

Type of change

  • Bug fix
  • New feature
  • Breaking change
  • Refactoring
  • Analyzer / diagnostic change
  • Tests
  • Documentation
  • Build / CI / tooling

Changes

  • Add a gated net472 leg to the three test projects that cover the shipped libraries (FirstClassErrors.UnitTests, FirstClassErrors.PropertyTests, FirstClassErrors.RequestBinder.PropertyTests), wired through a shared build/Net472TestFloor.props. It is opt-in via EnableNet472Floor, so the ordinary build and the local inner loop stay net10.0-only.
  • The net472 leg supplies what .NET Framework needs and nothing more: LangVersion=latest (net472 defaults C# to 7.3, which rejects nullable), OutputType=Exe (xUnit v3 on .NET Framework), the Microsoft.NETFramework.ReferenceAssemblies pack (hermetic compilation off-Windows), and an internal IsExternalInit polyfill for the records/init a few test models use. The shipped libraries use none of these and are untouched.
  • Exclude the System.Text.Json round-trip test on net472 (a worker-side concern that never runs there). RequestBinder.UnitTests stays net10.0-only because its fixtures bind DateOnly (a .NET 6+ type absent from .NET Framework), so RequestBinder reaches the floor through its property tests. Pin AsEnumerable().Reverse() so the LINQ overload wins over the void MemoryExtensions.Reverse(Span<T>) once System.Memory enters the net472 graph.
  • Add the Windows framework-floor CI job that runs the three legs on .NET Framework 4.7.2, and extend the weekly canary to run the library suites on the next .NET preview (best-effort, non-blocking).
  • Record ADR-0022 (EN + FR, Accepted) and correct the README from 4.6.1+ to 4.7.2+.

Testing

  • dotnet build FirstClassErrors.sln — clean (0 warnings, 0 errors).
  • dotnet test FirstClassErrors.sln — 1070 tests pass on net10.0.
  • Analyzer tests pass (FirstClassErrors.Analyzers.UnitTests) — 85, run as part of the solution test.

Not run locally: the net472 legs are compiled and pass on the Linux dev environment, but .NET Framework does not run there — their execution happens on the new framework-floor job (Windows). The canary preview run is best-effort and non-blocking by design.

Documentation

  • Public API / error documentation updated
  • README / doc/ updated
  • French translation (doc/handwritten/for-users/README.fr.md) updated if user-facing behavior changed
  • No documentation change required

ADR-0022 is added in both English and French (the bilingual ADR convention). No user-facing behaviour changed in README.fr.md; the only support claim lives in the NuGet README, which has no French counterpart.

Architecture decisions

  • No architectural decision in this pull request
  • New decision recorded — ADR-0022 (Accepted by the maintainer in this change)
  • Supersedes an existing ADR — successor proposed, status not flipped: ADR-____
  • ⚠️ Conflicts with an existing ADR — flagged for the maintainer: ADR-____

ADR-0022 refines the incidental "4.6.1" line in ADR-0002 without superseding it — ADR-0002's decision is the tooling floor, not the library one.

Related issues

None.

🤖 Generated with Claude Code

https://claude.ai/code/session_012UcTTqS7hwNHhgxdm8wQU4


Generated by Claude Code

claude added 3 commits July 19, 2026 20:07
The libraries target netstandard2.0 and were advertised as running on
.NET Framework 4.6.1+, but 4.6.1 is only the theoretical minimum: the
netstandard2.0 facades ship in-box from 4.7.2, and the claim was never
verified in CI. Record 4.7.2 as the supported floor and correct the
README to match.

ADR-0022 refines the incidental 4.6.1 line in ADR-0002 (whose decision
is the tooling floor, not the library one) without superseding it, and
is recorded as Accepted per the maintainer's decision.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012UcTTqS7hwNHhgxdm8wQU4
netstandard2.0 is only a compile contract; running on the real .NET
Framework CLR is what proves the libraries behave there. Add a gated
net472 leg to the three test projects that cover the shipped libraries,
wired through the shared build/Net472TestFloor.props.

EnableNet472Floor keeps net472 out of the ordinary build and the local
inner loop; only the opt-in CI job adds it. The leg needs
LangVersion=latest (net472 defaults C# to 7.3, which rejects nullable),
OutputType=Exe (xUnit v3 on .NET Framework), the reference-assembly pack
to compile off-Windows, and an internal IsExternalInit polyfill for the
records/init a few test models use. The shipped libraries use none of
these and are untouched.

The System.Text.Json round-trip test is excluded on net472 (a worker
concern that never runs there); RequestBinder reaches the floor through
its property tests, since its unit-test fixtures bind DateOnly (net6+).
AsEnumerable().Reverse() pins the LINQ overload over the void
MemoryExtensions.Reverse(Span) that wins once System.Memory is present.

Two ErrorCode/ErrorContextKey blank-input tests pinned the exact
ArgumentException.Message, whose parameter suffix .NET Framework renders
as "...\nParameter name: x" where modern .NET uses "... (Parameter 'x')";
they now assert the message content and ParamName — the contract, stable
on both. The library guards are unchanged.

See ADR-0022.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012UcTTqS7hwNHhgxdm8wQU4
Add a Windows framework-floor job that runs the three net472-enabled
test projects on .NET Framework 4.7.2 (EnableNet472Floor plus -f net472),
proving the libraries load and pass on that runtime — the guarantee the
Linux/net10 legs cannot give. A per-project loop keeps net472 off the
net10-only projects a solution-wide -f would otherwise force it onto.

Extend the weekly canary to run the library suites on the next .NET
preview: the build stays on the .NET 10 SDK and only the test host rolls
forward, so a runtime regression that breaks behaviour surfaces before
that major ships. Best-effort and non-blocking, like the rest of the
canary.

See ADR-0022.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012UcTTqS7hwNHhgxdm8wQU4
@Reefact
Reefact force-pushed the claude/dotnet-framework-47x-tests-qvx37f branch from bf48811 to 1166132 Compare July 19, 2026 20:08
@Reefact
Reefact merged commit 1732c33 into main Jul 19, 2026
15 checks passed
@Reefact
Reefact deleted the claude/dotnet-framework-47x-tests-qvx37f branch July 19, 2026 20:12
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.

2 participants