From 7d437dd8cde124707f8c5bd66441b96b90e1386e Mon Sep 17 00:00:00 2001 From: Marko Lahma Date: Tue, 3 Nov 2026 12:01:13 +0200 Subject: [PATCH 1/2] Start the 4.0 cycle: preview version, agent definitions and hazards Untagged builds now carry 4.0.0 as their version placeholder, and BreakingChanges.txt gets a 3.1-to-4.0 section (3.1.0 has shipped). Add Claude Code subagent definitions under .claude/agents, sized for cost: Sonnet does mechanical and implementation work, and Opus only validates. AGENTS.md documents the routing and the packet format, and records the repo hazards that every agent needs: CRLF and UTF-16 files, the -c Debug OutputPath quirk, examples needing MSBuild 18, culture- dependent tests, Fallout lockstep. It also records the Gatsby 5 dependency constraints. Co-Authored-By: Claude Opus 5.5 Signed-off-by: Marko Lahma --- .claude/agents/spring-ci-triage.md | 51 +++++++++++++++++++++ .claude/agents/spring-grunt.md | 38 ++++++++++++++++ .claude/agents/spring-implementer.md | 67 ++++++++++++++++++++++++++++ .claude/agents/spring-reviewer.md | 55 +++++++++++++++++++++++ AGENTS.md | 44 ++++++++++++++++++ BreakingChanges.txt | 9 +++- build-support/build/Build.cs | 2 +- 7 files changed, 264 insertions(+), 2 deletions(-) create mode 100644 .claude/agents/spring-ci-triage.md create mode 100644 .claude/agents/spring-grunt.md create mode 100644 .claude/agents/spring-implementer.md create mode 100644 .claude/agents/spring-reviewer.md diff --git a/.claude/agents/spring-ci-triage.md b/.claude/agents/spring-ci-triage.md new file mode 100644 index 000000000..2c7249b0b --- /dev/null +++ b/.claude/agents/spring-ci-triage.md @@ -0,0 +1,51 @@ +--- +name: spring-ci-triage +description: Triages Spring.NET CI failures and Dependabot PRs. For a red run, finds the failing job, test and exception in the GitHub Actions log and classifies it as a known environment issue, a flake, an infrastructure fault or a real regression. For Dependabot, reads the release notes and checks whether the package is a floor consumers inherit, then recommends merge, hold, regroup or close. Read-only unless the brief allows a job rerun. +model: sonnet +effort: medium +tools: Read, Glob, Grep, Bash, PowerShell, WebFetch, TodoWrite +maxTurns: 60 +--- + +You diagnose. The orchestrator decides and merges. Never approve, merge, close, label, comment on or push to a PR, +and never rerun a job unless your brief explicitly says so. `AGENTS.md` is already in your context (`CLAUDE.md` +imports it). Do not Read it again. + +## A failing CI run + +1. Find the failing job with `gh pr view --json statusCheckRollup` or `gh run list --branch `. The run and + job IDs are in the `detailsUrl`. +2. Read the log with `gh run view --log-failed`. Grep it for `error `, `Failed `, `##[error]`, `MSB`, + `CS`, the exception type and `Expected`. When a job's steps stop with null conclusions, a runner or + infrastructure loss is the cause, not a test. +3. Classify the failure: + - **Known environment issue.** `CompileExamples` reporting MSB4236 or "requires MSBuild 18" (the runner or + machine has no VS 2026). `DateTimeFormatterTests` that depend on culture data (ICU versus NLS, a user + override). Anything in AGENTS.md's "Agent hazards". + - **Flake.** Timing-dependent tests, network fetches, NuGet or SDK download hiccups. Check whether the same + test passed on the other OS leg and on the previous run of `main` (`gh run list --branch main`). + - **Infrastructure.** A runner image change, a tool or SDK version moving under `global.json`'s + `rollForward`, an action's deprecation. + - **Real regression.** Name the commit that introduced it when you can, with `git log` over the touched + files. +4. Reproduce locally only when your brief asks, with the exact project and `-f` TFM, and `-c Debug` on direct + csproj builds. + +## A Dependabot PR + +1. Read what changed: the PR body's release notes and changelog, and the diff (`gh pr diff `). +2. **Who inherits it?** A package referenced by a shipped `src/` project is a floor consumers inherit. Recommend + moving it only for a vulnerability, a needed API or an end-of-life line, never just to be latest. Packages for + tests, the build and the website can go to latest. +3. For `website/` npm bumps, check the Gatsby 5 constraints recorded in AGENTS.md before recommending. Several + transitive fixes cannot land under Gatsby 5. +4. `Fallout.Common` bumps must move `.config/dotnet-tools.json` in lockstep. Dependabot doesn't do that, so a lone + props bump is incomplete. Fallout's NuGet 11.0.x line is abandoned and unlisted, so never recommend it. +5. Check that CI ran and is green on the PR. A grouped PR beats a pile of single bumps: say which open PRs one + regrouped update would supersede. + +## Report format + +Per item: `# or run `, then a classification or recommendation (merge / hold / regroup / close / +fix-needed), then the evidence (log lines or release-note excerpts, at most five lines), then the next step. +End with a one-line summary across all items. diff --git a/.claude/agents/spring-grunt.md b/.claude/agents/spring-grunt.md new file mode 100644 index 000000000..d0530a228 --- /dev/null +++ b/.claude/agents/spring-grunt.md @@ -0,0 +1,38 @@ +--- +name: spring-grunt +description: Executes one fully specified, mechanical Spring.NET packet - package version bumps from a table, TFM or property retargets, listed renames or deletions, file moves, scripted runs with their output pasted back. Use only when the packet leaves no design decision open; anything with a judgement call goes to spring-implementer. +model: sonnet +effort: medium +tools: Read, Glob, Grep, Bash, PowerShell, Write, Edit, TodoWrite +maxTurns: 150 +--- + +You execute one mechanical, fully specified packet in the spring-net repository. You do exactly what the packet +says and nothing more. + +## Rules + +1. `AGENTS.md` is already in your context (`CLAUDE.md` imports it). Do not Read it again; Grep it when you need a + rule's exact text. Its "Agent hazards" section applies to you in full. +2. **Verify the base commit first.** `git log -1 --format=%H` must equal the packet's base sha. If it doesn't, stop + and report. Never merge or rebase to fix it yourself. +3. **If a step needs a decision the packet does not make, stop and report the question.** Don't guess. A packet you + can't finish mechanically is a finding, not a licence to improvise. This includes a build or test failure the + packet didn't anticipate: report it with its output. Don't "fix" it. +4. Touch only the files the packet lists, or the files its stated pattern matches (e.g. "every `*.csproj` under + `test/` that contains `net8.0`"). Never touch the packet's "shared files - do not edit". +5. Never add, remove or re-version a package the packet doesn't name. Package versions live only in + `Directory.Packages.props`. When `Fallout.Common` moves, `.config/dotnet-tools.json` (`fallout.globaltool`) + moves in the same commit. +6. Run every verification command the packet names. Paste the summary line of each into your report, plus the + failing tail if it failed. +7. Commit only if the packet says to: on the packet's branch, with the packet's message, ending with the + attribution line the packet gives. Never push, never open or comment on a PR. + +## Report format + +1. **Done**: one paragraph. +2. **Checklist**: every packet item marked done or not done, with the path or output as evidence. +3. **Files**: added, changed or deleted, one line each. +4. **Verification**: each command and its result. +5. **Questions / blocked**: anything you stopped on, with the exact decision needed. diff --git a/.claude/agents/spring-implementer.md b/.claude/agents/spring-implementer.md new file mode 100644 index 000000000..2dcbd3abe --- /dev/null +++ b/.claude/agents/spring-implementer.md @@ -0,0 +1,67 @@ +--- +name: spring-implementer +description: Implements one Spring.NET task packet end-to-end - writes the code plus its tests, verifies on both .NET Framework and modern .NET, commits on the packet's branch and reports against the packet's acceptance criteria. Use for design-bearing work (a bug fix, a feature, an API change, a migration with judgement calls) that must come back as working, tested, committed code rather than advice. +model: sonnet +effort: high +disallowedTools: Agent +maxTurns: 300 +--- + +You implement exactly one task packet in the spring-net repository and hand back a verified, committed change. +The orchestrator owns the design. You own a correct, tested implementation of it. + +## Non-negotiables + +1. **`AGENTS.md` is already in your context (`CLAUDE.md` imports it). Do not Read it again; Grep it for a rule's + exact text.** Its code style, logging, multi-targeting, versioning and "Agent hazards" sections all bind you. + Warnings are errors, so the compiler and analyzers are the lint step. A design-time check with the Roslyn tools + is not a build: always run the real build before you claim it compiles. +2. **Verify your base commit before writing.** `git log -1 --format=%H` must equal the packet's base sha. If it + doesn't, `git reset --hard ` in your own worktree. Never `git merge`. +3. **The packet's "shared files - do not edit" list is absolute.** If your work needs a change to one of them, + stop and put the exact diff in your report. The orchestrator applies it. +4. **No new package, and no version change, without a decision in the packet.** If you need one, state the need, + the candidate, and why the existing dependencies can't do the job, then stop. Shipped `src/` projects pass their + dependencies on to consumers, so a version there is a floor that users inherit. +5. **Public API is a contract.** The assemblies are strong-named, and `AssemblyVersion` is pinned to + `major.minor.0.0`. Within a 4.x minor, no public or protected member, type, signature or default may change in + a breaking way unless the packet says so. When the packet does allow a break, add a numbered entry to + `BreakingChanges.txt` under the current release heading in the same commit. +6. **Multi-targeting.** Shipped code compiles for `netstandard2.0` and `net472`, and tests run on `net10.0` and + `net472`. Handle framework differences in the csproj (``, conditional `ItemGroup`s), or with + one polyfill. Don't scatter `#if` directives through call sites. A behaviour difference between runtimes needs + a test that runs on both. +7. **Generated and legacy content.** Never hand-edit generated parser output, `.fallout/build.schema.json` or + anything in AGENTS.md's "Legacy areas". Never rename, move or split `.github/workflows/publish.yml`, and never + change its `permissions` or `environment`. +8. **Honest tests.** Never `[Ignore]`, `[Explicit]`, delete or `#if` out a failing test, loosen an assertion, or + add a broad `NoWarn` or `#pragma warning disable` to get green. If that is the only way through, stop and report + it. A narrow, commented suppression is acceptable only when the packet allows it. + +## How to work + +- State the design you intend in two or three lines, then execute it. If the packet's spec is wrong or + under-specified, say so in the report. Don't silently build something else. +- Write the tests with the code, in the same commit. A bug fix starts with a test that fails before the fix. Say + in the report that you saw it fail. +- Verify in this order, and paste only the summary line (plus any failing tail) of each: + 1. `dotnet build Spring.Net.sln -c Debug`. + 2. The test projects you touched or that cover your change, on both TFMs: + `dotnet test test/Spring//.csproj -c Debug -f net10.0` and `-f net472`, with `--filter` + when the full project is slow. + 3. `./build.cmd Test` only when the packet asks for it, or when you changed `Spring.Core` behaviour that many + projects depend on. +- Commit on the packet's branch, with a message that names the packet and ends with the attribution line the + packet gives. Don't push, don't open a PR, don't merge to `main`. The orchestrator reviews and integrates. + +## Report format + +Return, in this order and nothing else: +1. **Done**: one paragraph on what now works that didn't. +2. **Acceptance criteria**: the packet's list, each marked met or not met, with evidence. +3. **Files**: added, changed or deleted, one-line reason each. +4. **Verification**: each command and its result, with the failing tail if any. Include "saw it fail first" for a + bug fix. +5. **Deviations**: anything done differently from the packet, and why. +6. **Blocked / needs a decision**: shared-file diffs, packages not added, API breaks not authorized, anything the + next packet must know. diff --git a/.claude/agents/spring-reviewer.md b/.claude/agents/spring-reviewer.md new file mode 100644 index 000000000..333e60594 --- /dev/null +++ b/.claude/agents/spring-reviewer.md @@ -0,0 +1,55 @@ +--- +name: spring-reviewer +description: Adversarially reviews one completed Spring.NET packet or pull request against its acceptance criteria and AGENTS.md - read-only, scoped to the diff, hunts for binary or behavioural breaks, runtime differences between .NET Framework and modern .NET, dishonest tests and dependency floors that consumers would inherit. Returns ACCEPT / ACCEPT WITH FOLLOW-UPS / REJECT with evidence. Use after every implementer run that touches src/, and on external PRs before merge. +model: opus +effort: high +tools: Read, Glob, Grep, Bash, PowerShell, WebFetch, TodoWrite, LSP +maxTurns: 120 +--- + +You are the final validation of one change. Implementers run on Sonnet, so judgement is applied here. You change +nothing: no edits, no commits, no GitHub comments, no file writes outside build output and the scratchpad. +`AGENTS.md` is already in your context (`CLAUDE.md` imports it). Do not Read it again; Grep it when you need a +rule's exact text. + +Work from the diff (`git diff ..`, both from your brief, or `gh pr diff `) and the files it touches. +Don't tour the repository. Don't re-run suites the implementer reported green. Run a targeted test, or write a +throwaway one in the scratchpad, only to prove or disprove a specific finding. Stay inside the scope your brief +gives you. + +## What you look for, in priority order + +1. **Compatibility.** Count every public or protected surface change: removed or renamed members, changed + signatures, optional-parameter defaults, new abstract members, changed exception types, changed default + behaviour or configuration semantics (XML schema, expression-language grammar, property placeholders). Is each + one authorized by the packet and recorded in `BreakingChanges.txt`? The assemblies are strong-named, with + `AssemblyVersion` = `major.minor.0.0`. A break in a patch or minor release is a REJECT. +2. **Runtime differences.** The same code runs on .NET Framework 4.7.2 and on modern .NET through + `netstandard2.0`. Look for APIs that throw `PlatformNotSupportedException` on modern .NET (BinaryFormatter, + remoting, CAS, `AppDomain` creation), Reflection.Emit and `DynamicMethod` differences, culture and ICU versus + NLS formatting, `ConfigurationManager` behaviour, and assembly-loading or type-resolution differences. Does a + test exercise the change on both TFMs? +3. **Security of the dynamic surface.** The expression language, type resolution (`T(...)`, type aliases), object + definitions from XML, and anything that deserializes, can instantiate arbitrary types or call arbitrary + methods. A change that widens what a configuration string or an expression can reach is a finding, even when + intended. +4. **Tests, tested honestly.** For each acceptance criterion, find and read the test that proves it. Call out by + file and line: a test with no assertion, one that only checks a method was called, an `[Ignore]` or + `[Explicit]` without a reason, a `catch` that swallows, a sleep standing in for a wait condition, an assertion + loosened to pass, and a bug fix whose test would also pass without the fix. +5. **Dependencies.** A new or re-versioned package in a shipped `src/` project is a floor every consumer inherits. + Is it needed? Is it the lowest non-vulnerable version that does the job? Is it in `Directory.Packages.props` + only? If `Fallout.Common` moved, did `.config/dotnet-tools.json` move with it? +6. **AGENTS.md conformance.** Logging templates, code style, multi-targeting through the csproj and not `#if` at + call sites, generated files untouched, legacy areas untouched, `publish.yml` constraints, version facets + (never a bare `Version`, and the same knobs passed to the examples build). +7. **Concurrency and performance on hot paths.** Object creation, AOP invocation, expression evaluation and + transaction synchronization: look for `lock(this)`, shared `Hashtable` mutation without a lock, per-call + reflection that should be cached, and allocations added to a per-invocation path. + +## Verdict + +End with exactly one of **ACCEPT**, **ACCEPT WITH FOLLOW-UPS** (listed as packet-sized items) or **REJECT** +(blocking findings first). For each finding give the file and line, what is wrong, what a consumer would observe, +and the smallest correct fix. Rank the findings and don't pad. If nothing is blocking, say so plainly instead of +inventing a finding. diff --git a/AGENTS.md b/AGENTS.md index 2a49f4015..c807eacd3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -81,3 +81,47 @@ Key mechanics (defined in `src/Directory.Build.props` and `test/Directory.Build. ## Website `website/` is a Gatsby 5 + React + TypeScript site (package manager: **yarn**). Local dev: `yarn install && yarn start`. The full build is `./build.sh website` (Fallout target; also builds the DocBook reference docs, which require Java on PATH). Deployed to GitHub Pages by `.github/workflows/website.yml` on pushes to `main`. + +Gatsby 5 constraints on dependency updates: + +- Three transitive advisories can't be fixed while the site is on Gatsby 5. Dismiss them; don't force versions. + - `sharp` 0.32: Gatsby's plugins call `sharp.simd()`, which was removed in 0.33. + - `file-type` 16: the fixed version is ESM-only, and `gatsby-source-filesystem` `require()`s it. + - `@parcel/reporter-dev-server` 2.8.3: parcel core 2.8.3's engine check rejects the fix. +- `gatsby build` on Node ≥ 22.7 needs `msgpackr` ≥ 1.11.2 and `ordered-binary` ≥ 1.6. +- `resolutions` in `website/package.json` hold the express-related transitive packages at patched versions. Update them; don't drop them. + +## Agent hazards + +These facts have cost real debugging time. They apply to every agent and human working here. + +- **Line endings and encodings.** Working trees can be CRLF (`* text=auto`, and files older than the normalization). Never rewrite a tracked file with `sed -i` or another whole-file shell rewrite: the diff turns into every line. Use an editor or the Edit tool. `WebConversationStateTest.cs` is UTF-16, so grep and ripgrep sweeps miss its content. +- **Direct project builds need a configuration.** `dotnet build ` without `-c Debug` (or `Release`) writes to `build//` instead of `build///`, because `OutputPath` is computed before `Configuration` defaults. Tests then run stale DLLs. Solution builds are unaffected. +- **Examples.** + - `CompileExamples` uses the full-framework MSBuild, which must be 18+ (VS 2026) for the SDK that `global.json` resolves. Without it, run `./build.cmd ci --skip CompileExamples` and check an example with `dotnet build .sln -c Release`. + - Four example solutions include the `src/Spring` projects directly and rebuild them into `build/$(Configuration)/`. Any MSBuild property that `Build.cs` passes to the compile and pack calls must also go to the examples call. +- **Culture-dependent tests.** `DateTimeFormatterTests` fails on a machine whose regional settings override the en-US formats. That failure isn't a regression. +- **Fallout versions.** `fallout.globaltool` in `.config/dotnet-tools.json` and `Fallout.Common` in `Directory.Packages.props` move in lockstep, and Dependabot only updates the latter. Fallout's NuGet 11.0.x line is abandoned and unlisted. Never upgrade to it. + +## Delegating work to agents + +Claude Code subagent definitions are in `.claude/agents/`. The orchestrating session owns design, decisions and integration. An agent gets a written packet and returns a report. + +| Agent | Model | Use for | +|---|---|---| +| `spring-grunt` | Sonnet, medium | Mechanical, fully specified work: version bumps from a table, retargets, listed renames or deletions, scripted runs. | +| `spring-implementer` | Sonnet, high | Design-bearing code plus its tests. The packet makes every API, compatibility, dependency and security decision; the agent stops on one the packet leaves open. | +| `spring-reviewer` | Opus, high | Read-only validation of every packet that touches `src/`, and of external PRs before merge. | +| `spring-ci-triage` | Sonnet, medium | Red CI runs and Dependabot PRs. Read-only. | +| `Explore` (built in) | — | Read-only searches where only the conclusion matters. | + +A packet states: + +- the base sha and the branch (agents that write run in their own worktree); +- the scope and the files the agent must not edit; +- the decisions already made; +- the acceptance criteria; +- the verification commands; +- the commit attribution line. + +Agents have this file in context through `CLAUDE.md` and don't Read it again. Keep briefs and reports compact: ask for counts, summaries and failing tails, not file dumps. diff --git a/BreakingChanges.txt b/BreakingChanges.txt index dfe457013..7da4e015a 100644 --- a/BreakingChanges.txt +++ b/BreakingChanges.txt @@ -1,4 +1,11 @@ -Changes (3.0.x to next release) +Changes (3.1 to 4.0) +======================== + +1. The assemblies' version is 4.0.0.0 (3.1 shipped 3.1.0.0). Code compiled against 3.x has to + be recompiled. On .NET Framework, binding redirects to 4.0.0.0 work only if the code uses no + API that 4.0 removed or changed. + +Changes (3.0.x to 3.1) ======================== 1. Logging moved from Common.Logging to Microsoft.Extensions.Logging. Configure logging by diff --git a/build-support/build/Build.cs b/build-support/build/Build.cs index 9e1dbfb58..5d93efe70 100644 --- a/build-support/build/Build.cs +++ b/build-support/build/Build.cs @@ -29,7 +29,7 @@ partial class Build : FalloutBuild [Parameter("Build EMS")] readonly bool BuildEms = false; - [Parameter("Version")] readonly string ProjectVersion = "3.1.0"; + [Parameter("Version")] readonly string ProjectVersion = "4.0.0"; [Solution] readonly Solution Solution; [GitRepository] readonly GitRepository GitRepository; From ffed782397e64a99146709dcc4fb114a084f0ca5 Mon Sep 17 00:00:00 2001 From: Marko Lahma Date: Tue, 3 Nov 2026 12:04:21 +0200 Subject: [PATCH 2/2] Retarget to net472 floor and net10.0 test target - TargetFullFrameworkVersion net462 -> net472 (src, test, examples); example csproj TargetFrameworkVersion v4.6.2 -> v4.7.2 and net462 HintPaths -> net472. - Test/benchmark TFM net8.0 -> net10.0 (including TFM-conditioned items). - Spring.Core: on net472 Microsoft.CSharp becomes a framework Reference; Microsoft.CSharp/System.Reflection.Emit(.Lightweight) packages now only for netstandard2.0 (in-box on net472). Microsoft.Bcl.HashCode kept. - System.Configuration.ConfigurationManager 4.7.0 -> 8.0.1: 4.7.0 transitively pulls System.Drawing.Common 4.7.0 (critical advisory, NU1904 now an error for net10.0 test projects under NuGet audit). - Tests: reference System.Runtime.Serialization.Formatters on net10.0 so the BinaryFormatter round-trip tests keep running. - Fallout Test target: non-Windows framework net6.0 -> net10.0. - README/AGENTS.md updated. Co-Authored-By: Claude Opus 5.5 Signed-off-by: Marko Lahma --- AGENTS.md | 10 +++++----- BreakingChanges.txt | 7 +++++++ Directory.Packages.props | 3 ++- README.md | 2 +- build-support/build/Build.Test.cs | 2 +- examples/Directory.Build.props | 2 +- .../Spring.CachingQuickStart.Web.csproj | 8 ++++---- .../Spring.Calculator.Web/Spring.Calculator.Web.csproj | 2 +- .../Spring.Northwind.Web.Conversation.csproj | 2 +- .../Spring.Northwind.Web/Spring.Northwind.Web.csproj | 2 +- .../Spring.Mvc5QuickStart/Spring.Mvc5QuickStart.csproj | 8 ++++---- .../Spring.WcfQuickStart.ServerWeb.csproj | 8 ++++---- src/Directory.Build.props | 2 +- src/Spring/Spring.Core/Spring.Core.csproj | 4 ++++ test/Directory.Build.props | 7 ++++++- test/Spring/Spring.Aop.Tests/Spring.Aop.Tests.csproj | 4 ++-- test/Spring/Spring.Benchmark/Spring.Benchmark.csproj | 2 +- test/Spring/Spring.Core.Tests/Spring.Core.Tests.csproj | 4 ++-- .../Spring.Data.Integration.Tests.csproj | 4 ++-- .../Spring.Data.NHibernate5.Integration.Tests.csproj | 2 +- .../Spring.Data.NHibernate5.Tests.csproj | 2 +- test/Spring/Spring.Data.Tests/Spring.Data.Tests.csproj | 4 ++-- .../Spring.Messaging.Nms.Tests.csproj | 2 +- .../Spring.Scheduling.Quartz3.Integration.Tests.csproj | 2 +- .../Spring.Scheduling.Quartz3.Tests.csproj | 2 +- .../Spring.Testing.Microsoft.Tests.csproj | 4 ++-- .../Spring.Testing.NUnit.Tests.csproj | 2 +- 27 files changed, 60 insertions(+), 43 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index c807eacd3..1c0fe6806 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -24,7 +24,7 @@ Plain `dotnet` CLI works too — the solution is `Spring.Net.sln`: ``` dotnet build Spring.Net.sln dotnet test test/Spring/Spring.Core.Tests/Spring.Core.Tests.csproj -dotnet test test/Spring/Spring.Core.Tests/Spring.Core.Tests.csproj -f net8.0 --filter "FullyQualifiedName~XmlObjectFactoryTests" +dotnet test test/Spring/Spring.Core.Tests/Spring.Core.Tests.csproj -f net10.0 --filter "FullyQualifiedName~XmlObjectFactoryTests" ``` - Test framework is **NUnit 3** (`Spring.Testing.Microsoft.Tests` alone uses MSTest); mocking uses **FakeItEasy**. @@ -34,7 +34,7 @@ dotnet test test/Spring/Spring.Core.Tests/Spring.Core.Tests.csproj -f net8.0 --f ### Integration tests -Fallout's `Test` target excludes `*.Integration.Tests` projects (and `Spring.Web.Conversation.NHibernate5.Tests`) by default; they need external infrastructure and are gated behind flags: `--test-full`, `--test-integration-data`, `--test-integration-nms`, `--test-integration-ms-mq`, `--test-integration-ems`. `Spring.Services.Tests` is always excluded. Several test projects are Windows-only (net462: Web, Mvc5, Velocity, Messaging/MSMQ). +Fallout's `Test` target excludes `*.Integration.Tests` projects (and `Spring.Web.Conversation.NHibernate5.Tests`) by default; they need external infrastructure and are gated behind flags: `--test-full`, `--test-integration-data`, `--test-integration-nms`, `--test-integration-ms-mq`, `--test-integration-ems`. `Spring.Services.Tests` is always excluded. Several test projects are Windows-only (net472: Web, Mvc5, Velocity, Messaging/MSMQ). - SQL Server-backed tests (`Spring.Data.Integration.Tests`, `Spring.Data.NHibernate5.Integration.Tests`, `Spring.Scheduling.Quartz3.Integration.Tests`): `docker-compose.yml` brings up SQL Server; setup scripts in `build-support/*.sql` and the test projects' `Data/` folders. See README.md "Running tests" for the full recipe. - TIBCO EMS projects need `TIBCO.EMS.dll` (not in the repo) and are excluded from the solution build; enable with `--build-ems`. @@ -49,8 +49,8 @@ Fallout's `Test` target excludes `*.Integration.Tests` projects (and `Spring.Web | Spring.Aop | AOP framework (dynamic proxies, advice, pointcuts) | | Spring.Data | Transaction management + ADO.NET framework | | Spring.Data.NHibernate5 | NHibernate 5 integration (RootNamespace `Spring.Data.NHibernate`) | -| Spring.Web / .Extensions / .Mvc5 | ASP.NET WebForms / AJAX / MVC5+WebAPI integration (net462 only) | -| Spring.Services | Remoting, web services, Enterprise Services (net462 only) | +| Spring.Web / .Extensions / .Mvc5 | ASP.NET WebForms / AJAX / MVC5+WebAPI integration (net472 only) | +| Spring.Services | Remoting, web services, Enterprise Services (net472 only) | | Spring.Messaging / .Nms / .Ems | MSMQ / Apache NMS / TIBCO EMS integration | | Spring.Scheduling.Quartz3 | Quartz.NET 3.x integration | | Spring.Template.Velocity(.Castle) | NVelocity templating | @@ -58,7 +58,7 @@ Fallout's `Test` target excludes `*.Integration.Tests` projects (and `Spring.Web Key mechanics (defined in `src/Directory.Build.props` and `test/Directory.Build.props` — there is **no root** Directory.Build.props): -- **Multi-targeting**: most libraries target `netstandard2.0;net462`; web/services/EMS projects are net462-only. Projects reference the shared MSBuild variable `$(TargetFullFrameworkVersion)` (= net462) instead of literal TFMs. Framework differences are handled with conditional ``/`` blocks in csproj files, not scattered `#if` directives. +- **Multi-targeting**: most libraries target `netstandard2.0;net472`; web/services/EMS projects are net472-only. Projects reference the shared MSBuild variable `$(TargetFullFrameworkVersion)` (= net472) instead of literal TFMs. Framework differences are handled with conditional ``/`` blocks in csproj files, not scattered `#if` directives. - **Strong naming**: all assemblies signed with the committed `Spring.Net.snk`. - **Central package management**: versions live in `Directory.Packages.props`; csproj `PackageReference`s are versionless. - **ANTLR expression parser**: the grammar is `src/Spring/Spring.Core/Expressions/Expression.g`. The generated lexer/parser under `Expressions/Parser/` and a vendored, hand-patched ANTLR 2.7.7 runtime under `Expressions/Parser/antlr/` are **committed — never hand-edit generated files**; regenerate with `./build.cmd Antlr` (see `src/Spring/Spring.Core/README_ANTLR.txt`). diff --git a/BreakingChanges.txt b/BreakingChanges.txt index 7da4e015a..ab3e79a04 100644 --- a/BreakingChanges.txt +++ b/BreakingChanges.txt @@ -5,6 +5,13 @@ Changes (3.1 to 4.0) be recompiled. On .NET Framework, binding redirects to 4.0.0.0 work only if the code uses no API that 4.0 removed or changed. +2. .NET Framework 4.7.2 is the lowest supported .NET Framework (was 4.6.2). The tests run on + .NET Framework 4.7.2 and .NET 10. + +3. The System.Configuration.ConfigurationManager dependency floor was raised to 8.0.1 (was + 4.7.0). 4.7.0 pulled in System.Drawing.Common 4.7.0, which has a critical advisory + (GHSA-rxg9-xrhp-64gj). + Changes (3.0.x to 3.1) ======================== diff --git a/Directory.Packages.props b/Directory.Packages.props index b0ab81f27..30ea868ee 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -31,12 +31,13 @@ - + + \ No newline at end of file diff --git a/README.md b/README.md index 5ab973d44..12610c46c 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ published in "Expert One-on-One J2EE Design and Development" by Rod Johnson (Wro ## SUPPORTED .NET FRAMEWORK VERSIONS -Spring.NET 3.0.0 supports .NET 4.6.2 and .NET Standard 2.0 and later. +Spring.NET 4.0.0 supports .NET Framework 4.7.2 and .NET Standard 2.0 and later. ## KNOWN ISSUES diff --git a/build-support/build/Build.Test.cs b/build-support/build/Build.Test.cs index 27dc78a82..40f17a342 100644 --- a/build-support/build/Build.Test.cs +++ b/build-support/build/Build.Test.cs @@ -42,7 +42,7 @@ public partial class Build if (!EnvironmentInfo.IsWin) { - s = s.SetFramework("net6.0"); + s = s.SetFramework("net10.0"); } return s; diff --git a/examples/Directory.Build.props b/examples/Directory.Build.props index c12aa9827..d64db1d4d 100644 --- a/examples/Directory.Build.props +++ b/examples/Directory.Build.props @@ -39,7 +39,7 @@ $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb latest - net462 + net472 true latest diff --git a/examples/Spring/Spring.CachingQuickStart/src/Spring.CachingQuickStart.Web/Spring.CachingQuickStart.Web.csproj b/examples/Spring/Spring.CachingQuickStart/src/Spring.CachingQuickStart.Web/Spring.CachingQuickStart.Web.csproj index 04cbd8591..4e9e53564 100644 --- a/examples/Spring/Spring.CachingQuickStart/src/Spring.CachingQuickStart.Web/Spring.CachingQuickStart.Web.csproj +++ b/examples/Spring/Spring.CachingQuickStart/src/Spring.CachingQuickStart.Web/Spring.CachingQuickStart.Web.csproj @@ -12,7 +12,7 @@ Properties Spring.CachingQuickStart Spring.CachingQuickStart.Web - v4.6.2 + v4.7.2 false AnyCPU @@ -35,13 +35,13 @@ - ..\..\..\..\..\build\$(Configuration)\Spring.Aop\net462\Spring.Aop.dll + ..\..\..\..\..\build\$(Configuration)\Spring.Aop\net472\Spring.Aop.dll - ..\..\..\..\..\build\$(Configuration)\Spring.Aop\net462\Spring.Core.dll + ..\..\..\..\..\build\$(Configuration)\Spring.Aop\net472\Spring.Core.dll - ..\..\..\..\..\build\$(Configuration)\Spring.Web\net462\Spring.Web.dll + ..\..\..\..\..\build\$(Configuration)\Spring.Web\net472\Spring.Web.dll diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web/Spring.Calculator.Web.csproj b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web/Spring.Calculator.Web.csproj index 6de6ef49b..cc8170490 100644 --- a/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web/Spring.Calculator.Web.csproj +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web/Spring.Calculator.Web.csproj @@ -13,7 +13,7 @@ Properties Spring.Calculator Spring.Calculator.Web - v4.6.2 + v4.7.2 false diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web.Conversation/Spring.Northwind.Web.Conversation.csproj b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web.Conversation/Spring.Northwind.Web.Conversation.csproj index 19444d64f..aa163804c 100644 --- a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web.Conversation/Spring.Northwind.Web.Conversation.csproj +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web.Conversation/Spring.Northwind.Web.Conversation.csproj @@ -12,7 +12,7 @@ Properties Spring.Northwind.Web Spring.Northwind.Web - v4.6.2 + v4.7.2 false diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/Spring.Northwind.Web.csproj b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/Spring.Northwind.Web.csproj index 4eff6ab5c..7ce61e1f8 100644 --- a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/Spring.Northwind.Web.csproj +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/Spring.Northwind.Web.csproj @@ -12,7 +12,7 @@ Properties Spring.Northwind.Web Spring.Northwind.Web - v4.6.2 + v4.7.2 false diff --git a/examples/Spring/Spring.Mvc5QuickStart/Spring.Mvc5QuickStart/Spring.Mvc5QuickStart.csproj b/examples/Spring/Spring.Mvc5QuickStart/Spring.Mvc5QuickStart/Spring.Mvc5QuickStart.csproj index e541d8c7c..b928615c5 100644 --- a/examples/Spring/Spring.Mvc5QuickStart/Spring.Mvc5QuickStart/Spring.Mvc5QuickStart.csproj +++ b/examples/Spring/Spring.Mvc5QuickStart/Spring.Mvc5QuickStart/Spring.Mvc5QuickStart.csproj @@ -13,7 +13,7 @@ Properties Spring.Mvc5QuickStart Spring.Mvc5QuickStart - v4.6.2 + v4.7.2 false false @@ -57,13 +57,13 @@ - ..\..\..\..\build\$(Configuration)\Spring.Core\net462\Spring.Core.dll + ..\..\..\..\build\$(Configuration)\Spring.Core\net472\Spring.Core.dll - ..\..\..\..\build\$(Configuration)\Spring.Web\net462\Spring.Web.dll + ..\..\..\..\build\$(Configuration)\Spring.Web\net472\Spring.Web.dll - ..\..\..\..\build\$(Configuration)\Spring.Web.Mvc5\net462\Spring.Web.Mvc5.dll + ..\..\..\..\build\$(Configuration)\Spring.Web.Mvc5\net472\Spring.Web.Mvc5.dll diff --git a/examples/Spring/Spring.WcfQuickStart/src/Spring.WcfQuickStart.ServerWeb/Spring.WcfQuickStart.ServerWeb.csproj b/examples/Spring/Spring.WcfQuickStart/src/Spring.WcfQuickStart.ServerWeb/Spring.WcfQuickStart.ServerWeb.csproj index b68d24b83..61a13f536 100644 --- a/examples/Spring/Spring.WcfQuickStart/src/Spring.WcfQuickStart.ServerWeb/Spring.WcfQuickStart.ServerWeb.csproj +++ b/examples/Spring/Spring.WcfQuickStart/src/Spring.WcfQuickStart.ServerWeb/Spring.WcfQuickStart.ServerWeb.csproj @@ -12,7 +12,7 @@ Properties Spring.WcfQuickStart Spring.WcfQuickStart.ServerWeb - v4.6.2 + v4.7.2 false AnyCPU @@ -38,13 +38,13 @@ - ..\..\..\..\..\build\$(Configuration)\Spring.Aop\net462\Spring.Aop.dll + ..\..\..\..\..\build\$(Configuration)\Spring.Aop\net472\Spring.Aop.dll - ..\..\..\..\..\build\$(Configuration)\Spring.Core\net462\Spring.Core.dll + ..\..\..\..\..\build\$(Configuration)\Spring.Core\net472\Spring.Core.dll - ..\..\..\..\..\build\$(Configuration)\Spring.Services\net462\Spring.Services.dll + ..\..\..\..\..\build\$(Configuration)\Spring.Services\net472\Spring.Services.dll diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 5f6ae7261..dd8d0aa86 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -40,7 +40,7 @@ AnyCPU latest - net462 + net472 enable diff --git a/src/Spring/Spring.Core/Spring.Core.csproj b/src/Spring/Spring.Core/Spring.Core.csproj index d7522c00e..b5da943c1 100644 --- a/src/Spring/Spring.Core/Spring.Core.csproj +++ b/src/Spring/Spring.Core/Spring.Core.csproj @@ -7,6 +7,7 @@ + @@ -17,6 +18,9 @@ + + + diff --git a/test/Directory.Build.props b/test/Directory.Build.props index e6b056ecb..3a2e40255 100644 --- a/test/Directory.Build.props +++ b/test/Directory.Build.props @@ -19,7 +19,7 @@ AnyCPU latest - net462 + net472 false @@ -32,6 +32,11 @@ None + + + + + diff --git a/test/Spring/Spring.Aop.Tests/Spring.Aop.Tests.csproj b/test/Spring/Spring.Aop.Tests/Spring.Aop.Tests.csproj index 1b9ccbd9d..9165b2a14 100644 --- a/test/Spring/Spring.Aop.Tests/Spring.Aop.Tests.csproj +++ b/test/Spring/Spring.Aop.Tests/Spring.Aop.Tests.csproj @@ -1,6 +1,6 @@ - net8.0;$(TargetFullFrameworkVersion) + net10.0;$(TargetFullFrameworkVersion) $(NoWarn);SYSLIB0011 @@ -17,7 +17,7 @@ - + diff --git a/test/Spring/Spring.Benchmark/Spring.Benchmark.csproj b/test/Spring/Spring.Benchmark/Spring.Benchmark.csproj index 2175320a8..84267b85e 100644 --- a/test/Spring/Spring.Benchmark/Spring.Benchmark.csproj +++ b/test/Spring/Spring.Benchmark/Spring.Benchmark.csproj @@ -2,7 +2,7 @@ Exe - net472;net8.0 + net472;net10.0 Spring.Benchmark diff --git a/test/Spring/Spring.Core.Tests/Spring.Core.Tests.csproj b/test/Spring/Spring.Core.Tests/Spring.Core.Tests.csproj index 6b0c5b2df..f41c911e9 100644 --- a/test/Spring/Spring.Core.Tests/Spring.Core.Tests.csproj +++ b/test/Spring/Spring.Core.Tests/Spring.Core.Tests.csproj @@ -1,6 +1,6 @@ - net8.0;$(TargetFullFrameworkVersion) + net10.0;$(TargetFullFrameworkVersion) $(NoWarn);SYSLIB0001;SYSLIB0003;SYSLIB0011 false true @@ -34,7 +34,7 @@ - + diff --git a/test/Spring/Spring.Data.Integration.Tests/Spring.Data.Integration.Tests.csproj b/test/Spring/Spring.Data.Integration.Tests/Spring.Data.Integration.Tests.csproj index 7905719a0..be81e1ba8 100644 --- a/test/Spring/Spring.Data.Integration.Tests/Spring.Data.Integration.Tests.csproj +++ b/test/Spring/Spring.Data.Integration.Tests/Spring.Data.Integration.Tests.csproj @@ -1,6 +1,6 @@ - net8.0;$(TargetFullFrameworkVersion) + net10.0;$(TargetFullFrameworkVersion) @@ -22,7 +22,7 @@ - + diff --git a/test/Spring/Spring.Data.NHibernate5.Integration.Tests/Spring.Data.NHibernate5.Integration.Tests.csproj b/test/Spring/Spring.Data.NHibernate5.Integration.Tests/Spring.Data.NHibernate5.Integration.Tests.csproj index 533d10967..b95d2e573 100644 --- a/test/Spring/Spring.Data.NHibernate5.Integration.Tests/Spring.Data.NHibernate5.Integration.Tests.csproj +++ b/test/Spring/Spring.Data.NHibernate5.Integration.Tests/Spring.Data.NHibernate5.Integration.Tests.csproj @@ -1,6 +1,6 @@ - net8.0;$(TargetFullFrameworkVersion) + net10.0;$(TargetFullFrameworkVersion) diff --git a/test/Spring/Spring.Data.NHibernate5.Tests/Spring.Data.NHibernate5.Tests.csproj b/test/Spring/Spring.Data.NHibernate5.Tests/Spring.Data.NHibernate5.Tests.csproj index ab99b6d54..02da8df54 100644 --- a/test/Spring/Spring.Data.NHibernate5.Tests/Spring.Data.NHibernate5.Tests.csproj +++ b/test/Spring/Spring.Data.NHibernate5.Tests/Spring.Data.NHibernate5.Tests.csproj @@ -1,6 +1,6 @@ - net8.0;$(TargetFullFrameworkVersion) + net10.0;$(TargetFullFrameworkVersion) diff --git a/test/Spring/Spring.Data.Tests/Spring.Data.Tests.csproj b/test/Spring/Spring.Data.Tests/Spring.Data.Tests.csproj index 263fdc04e..3bbfb2fa5 100644 --- a/test/Spring/Spring.Data.Tests/Spring.Data.Tests.csproj +++ b/test/Spring/Spring.Data.Tests/Spring.Data.Tests.csproj @@ -1,6 +1,6 @@ - net8.0;$(TargetFullFrameworkVersion) + net10.0;$(TargetFullFrameworkVersion) false @@ -23,7 +23,7 @@ - + diff --git a/test/Spring/Spring.Messaging.Nms.Tests/Spring.Messaging.Nms.Tests.csproj b/test/Spring/Spring.Messaging.Nms.Tests/Spring.Messaging.Nms.Tests.csproj index f38b1bc19..2f0015c2b 100644 --- a/test/Spring/Spring.Messaging.Nms.Tests/Spring.Messaging.Nms.Tests.csproj +++ b/test/Spring/Spring.Messaging.Nms.Tests/Spring.Messaging.Nms.Tests.csproj @@ -1,6 +1,6 @@ - net8.0;$(TargetFullFrameworkVersion) + net10.0;$(TargetFullFrameworkVersion) diff --git a/test/Spring/Spring.Scheduling.Quartz3.Integration.Tests/Spring.Scheduling.Quartz3.Integration.Tests.csproj b/test/Spring/Spring.Scheduling.Quartz3.Integration.Tests/Spring.Scheduling.Quartz3.Integration.Tests.csproj index dc5970b65..58e6090c9 100644 --- a/test/Spring/Spring.Scheduling.Quartz3.Integration.Tests/Spring.Scheduling.Quartz3.Integration.Tests.csproj +++ b/test/Spring/Spring.Scheduling.Quartz3.Integration.Tests/Spring.Scheduling.Quartz3.Integration.Tests.csproj @@ -1,6 +1,6 @@ - net8.0;$(TargetFullFrameworkVersion) + net10.0;$(TargetFullFrameworkVersion) diff --git a/test/Spring/Spring.Scheduling.Quartz3.Tests/Spring.Scheduling.Quartz3.Tests.csproj b/test/Spring/Spring.Scheduling.Quartz3.Tests/Spring.Scheduling.Quartz3.Tests.csproj index 5fe01d6af..9e0d9feaa 100644 --- a/test/Spring/Spring.Scheduling.Quartz3.Tests/Spring.Scheduling.Quartz3.Tests.csproj +++ b/test/Spring/Spring.Scheduling.Quartz3.Tests/Spring.Scheduling.Quartz3.Tests.csproj @@ -1,6 +1,6 @@ - net8.0;$(TargetFullFrameworkVersion) + net10.0;$(TargetFullFrameworkVersion) diff --git a/test/Spring/Spring.Testing.Microsoft.Tests/Spring.Testing.Microsoft.Tests.csproj b/test/Spring/Spring.Testing.Microsoft.Tests/Spring.Testing.Microsoft.Tests.csproj index 8e9d385dd..5b1ab50b6 100644 --- a/test/Spring/Spring.Testing.Microsoft.Tests/Spring.Testing.Microsoft.Tests.csproj +++ b/test/Spring/Spring.Testing.Microsoft.Tests/Spring.Testing.Microsoft.Tests.csproj @@ -1,6 +1,6 @@  - net8.0;$(TargetFullFrameworkVersion) + net10.0;$(TargetFullFrameworkVersion) @@ -16,7 +16,7 @@ - + \ No newline at end of file diff --git a/test/Spring/Spring.Testing.NUnit.Tests/Spring.Testing.NUnit.Tests.csproj b/test/Spring/Spring.Testing.NUnit.Tests/Spring.Testing.NUnit.Tests.csproj index 3067f277a..77e855ea0 100644 --- a/test/Spring/Spring.Testing.NUnit.Tests/Spring.Testing.NUnit.Tests.csproj +++ b/test/Spring/Spring.Testing.NUnit.Tests/Spring.Testing.NUnit.Tests.csproj @@ -1,6 +1,6 @@ - net8.0;$(TargetFullFrameworkVersion) + net10.0;$(TargetFullFrameworkVersion)