diff --git a/Tomlyn.Extensions.Configuration.Tests/Tomlyn.Extensions.Configuration.Tests.csproj b/Tomlyn.Extensions.Configuration.Tests/Tomlyn.Extensions.Configuration.Tests.csproj
index 7422388..f9ffd11 100644
--- a/Tomlyn.Extensions.Configuration.Tests/Tomlyn.Extensions.Configuration.Tests.csproj
+++ b/Tomlyn.Extensions.Configuration.Tests/Tomlyn.Extensions.Configuration.Tests.csproj
@@ -1,28 +1,29 @@
-
-
-
- net6.0
- enable
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- PreserveNewest
-
-
-
-
+
+
+
+ net8.0;net10.0
+ enable
+ false
+ Exe
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
diff --git a/Tomlyn.Extensions.Configuration.sln b/Tomlyn.Extensions.Configuration.sln
deleted file mode 100644
index fbde3fd..0000000
--- a/Tomlyn.Extensions.Configuration.sln
+++ /dev/null
@@ -1,26 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tomlyn.Extensions.Configuration", "Tomlyn.Extensions.Configuration\Tomlyn.Extensions.Configuration.csproj", "{B9253898-AA34-4086-A8AD-0D6A35C36DF2}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tomlyn.Extensions.Configuration.Tests", "Tomlyn.Extensions.Configuration.Tests\Tomlyn.Extensions.Configuration.Tests.csproj", "{1A6BB949-3C9C-4685-8663-7DD5EDFFA5C4}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {79527171-556C-416D-AE11-D19828BE72FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {79527171-556C-416D-AE11-D19828BE72FF}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {79527171-556C-416D-AE11-D19828BE72FF}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {79527171-556C-416D-AE11-D19828BE72FF}.Release|Any CPU.Build.0 = Release|Any CPU
- {B9253898-AA34-4086-A8AD-0D6A35C36DF2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {B9253898-AA34-4086-A8AD-0D6A35C36DF2}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {B9253898-AA34-4086-A8AD-0D6A35C36DF2}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {B9253898-AA34-4086-A8AD-0D6A35C36DF2}.Release|Any CPU.Build.0 = Release|Any CPU
- {1A6BB949-3C9C-4685-8663-7DD5EDFFA5C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {1A6BB949-3C9C-4685-8663-7DD5EDFFA5C4}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {1A6BB949-3C9C-4685-8663-7DD5EDFFA5C4}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {1A6BB949-3C9C-4685-8663-7DD5EDFFA5C4}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
-EndGlobal
diff --git a/Tomlyn.Extensions.Configuration.slnx b/Tomlyn.Extensions.Configuration.slnx
new file mode 100644
index 0000000..ef1b851
--- /dev/null
+++ b/Tomlyn.Extensions.Configuration.slnx
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/Tomlyn.Extensions.Configuration/TomlConfigurationExtensions.cs b/Tomlyn.Extensions.Configuration/TomlConfigurationExtensions.cs
index 964fcdf..8733b87 100644
--- a/Tomlyn.Extensions.Configuration/TomlConfigurationExtensions.cs
+++ b/Tomlyn.Extensions.Configuration/TomlConfigurationExtensions.cs
@@ -52,6 +52,10 @@ public static IConfigurationBuilder AddTomlStream(this IConfigurationBuilder bui
{
throw new ArgumentNullException(nameof(builder));
}
+ if (stream == null)
+ {
+ throw new ArgumentNullException(nameof(stream));
+ }
return builder.Add(s => s.Stream = stream);
}
diff --git a/Tomlyn.Extensions.Configuration/TomlConfigurationFileParser.cs b/Tomlyn.Extensions.Configuration/TomlConfigurationFileParser.cs
index 8688d16..23f2659 100644
--- a/Tomlyn.Extensions.Configuration/TomlConfigurationFileParser.cs
+++ b/Tomlyn.Extensions.Configuration/TomlConfigurationFileParser.cs
@@ -20,8 +20,9 @@ public static IDictionary Parse(Stream input)
private IDictionary ParseStream(Stream input)
{
using var reader = new StreamReader(input);
- var doc = Toml.Parse(reader.ReadToEnd());
- VisitObject(doc.ToModel());
+ var model = TomlSerializer.Deserialize(reader.ReadToEnd())
+ ?? throw new FormatException("TOML deserialization returned null");
+ VisitObject(model);
return _data;
}
diff --git a/Tomlyn.Extensions.Configuration/Tomlyn.Extensions.Configuration.csproj b/Tomlyn.Extensions.Configuration/Tomlyn.Extensions.Configuration.csproj
index 7c264e9..82d72b2 100644
--- a/Tomlyn.Extensions.Configuration/Tomlyn.Extensions.Configuration.csproj
+++ b/Tomlyn.Extensions.Configuration/Tomlyn.Extensions.Configuration.csproj
@@ -1,21 +1,20 @@
- netstandard2.0;net461
+ netstandard2.0;net8.0;net10.0
TomlConfigurationProvider using Tomlyn
https://github.com/bugproof/Tomlyn.Extensions.Configuration
git
- 1.0.6
+ 2.0.0
toml;configuration
- https://raw.githubusercontent.com/xoofx/Tomlyn/master/img/logo.png
MIT
true
default
-
-
+
+
diff --git a/docs/superpowers/plans/2026-04-16-modernization.md b/docs/superpowers/plans/2026-04-16-modernization.md
new file mode 100644
index 0000000..2708deb
--- /dev/null
+++ b/docs/superpowers/plans/2026-04-16-modernization.md
@@ -0,0 +1,258 @@
+# Tomlyn.Extensions.Configuration Modernization Implementation Plan
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** Modernize the solution to .slnx, target net8.0/net10.0/netstandard2.0, upgrade Tomlyn to v2.3.0, and migrate tests to xUnit v3.
+
+**Architecture:** Replace legacy .sln with .slnx XML format. Update target frameworks (drop net461, add net8.0 and net10.0). Migrate Tomlyn API from `Toml.Parse().ToModel()` to `Toml.ToModel()`. Migrate test framework from xUnit v2 to xUnit v3.
+
+**Tech Stack:** .NET 10 SDK, Tomlyn 2.3.0, Microsoft.Extensions.Configuration 10.0.6, xUnit v3 (3.2.2), FluentAssertions 8.9.0
+
+---
+
+## File Map
+
+| File | Action | Responsibility |
+|------|--------|----------------|
+| `Tomlyn.Extensions.Configuration.sln` | Delete | Legacy solution format |
+| `Tomlyn.Extensions.Configuration.slnx` | Create | New XML solution format |
+| `Tomlyn.Extensions.Configuration/Tomlyn.Extensions.Configuration.csproj` | Modify | TFMs, version, deps |
+| `Tomlyn.Extensions.Configuration/TomlConfigurationFileParser.cs` | Modify | Tomlyn v2 API migration |
+| `Tomlyn.Extensions.Configuration.Tests/Tomlyn.Extensions.Configuration.Tests.csproj` | Modify | TFMs, xUnit v3, deps |
+
+---
+
+### Task 1: Convert solution file from .sln to .slnx
+
+**Files:**
+- Delete: `Tomlyn.Extensions.Configuration.sln`
+- Create: `Tomlyn.Extensions.Configuration.slnx`
+
+- [ ] **Step 1: Create the new .slnx file**
+
+Create `Tomlyn.Extensions.Configuration.slnx` with:
+
+```xml
+
+
+
+
+```
+
+- [ ] **Step 2: Delete the old .sln file**
+
+```bash
+rtk git rm Tomlyn.Extensions.Configuration.sln
+```
+
+- [ ] **Step 3: Verify the new solution loads**
+
+```bash
+rtk dotnet sln Tomlyn.Extensions.Configuration.slnx list
+```
+
+Expected: Both projects listed.
+
+---
+
+### Task 2: Update main project (.csproj)
+
+**Files:**
+- Modify: `Tomlyn.Extensions.Configuration/Tomlyn.Extensions.Configuration.csproj`
+
+- [ ] **Step 1: Update the .csproj file**
+
+Replace the entire contents of `Tomlyn.Extensions.Configuration/Tomlyn.Extensions.Configuration.csproj` with:
+
+```xml
+
+
+
+ netstandard2.0;net8.0;net10.0
+ TomlConfigurationProvider using Tomlyn
+ https://github.com/bugproof/Tomlyn.Extensions.Configuration
+ git
+ 2.0.0
+ toml;configuration
+ MIT
+ true
+ default
+
+
+
+
+
+
+
+
+```
+
+Changes from original:
+- `TargetFrameworks`: `netstandard2.0;net461` → `netstandard2.0;net8.0;net10.0`
+- `PackageVersion`: `1.0.6` → `2.0.0`
+- `PackageIconUrl` removed (deprecated NuGet property)
+- `Tomlyn`: `0.14.3` → `2.3.0`
+- `Microsoft.Extensions.Configuration.FileExtensions`: `5.0.0` → `10.0.6`
+
+- [ ] **Step 2: Verify the project restores**
+
+```bash
+rtk dotnet restore Tomlyn.Extensions.Configuration/Tomlyn.Extensions.Configuration.csproj
+```
+
+Expected: Restore succeeds for all three TFMs.
+
+---
+
+### Task 3: Migrate Tomlyn v2 API in TomlConfigurationFileParser.cs
+
+**Files:**
+- Modify: `Tomlyn.Extensions.Configuration/TomlConfigurationFileParser.cs:20-25`
+
+- [ ] **Step 1: Update the ParseStream method**
+
+In `Tomlyn.Extensions.Configuration/TomlConfigurationFileParser.cs`, replace lines 20-26:
+
+```csharp
+ private IDictionary ParseStream(Stream input)
+ {
+ using var reader = new StreamReader(input);
+ var doc = Toml.Parse(reader.ReadToEnd());
+ VisitObject(doc.ToModel());
+ return _data;
+ }
+```
+
+With:
+
+```csharp
+ private IDictionary ParseStream(Stream input)
+ {
+ using var reader = new StreamReader(input);
+ var model = Toml.ToModel(reader.ReadToEnd());
+ VisitObject(model);
+ return _data;
+ }
+```
+
+This changes the Tomlyn API call from the removed `Toml.Parse().ToModel()` (legacy v0.x) to `Toml.ToModel()` (v1.0+/v2.x). The return type is still `TomlTable`. Parse errors now throw `TomlException` instead of setting `doc.HasErrors`, but the caller (`TomlConfigurationProvider.Load`) already catches all exceptions and wraps them as `FormatException`.
+
+- [ ] **Step 2: Verify the main project builds for all TFMs**
+
+```bash
+rtk dotnet build Tomlyn.Extensions.Configuration/Tomlyn.Extensions.Configuration.csproj
+```
+
+Expected: Build succeeds for netstandard2.0, net8.0, and net10.0.
+
+---
+
+### Task 4: Update test project (.csproj) for xUnit v3
+
+**Files:**
+- Modify: `Tomlyn.Extensions.Configuration.Tests/Tomlyn.Extensions.Configuration.Tests.csproj`
+
+- [ ] **Step 1: Update the test .csproj file**
+
+Replace the entire contents of `Tomlyn.Extensions.Configuration.Tests/Tomlyn.Extensions.Configuration.Tests.csproj` with:
+
+```xml
+
+
+
+ net8.0;net10.0
+ enable
+ false
+ Exe
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
+```
+
+Changes from original:
+- `TargetFramework`: `net6.0` → `TargetFrameworks`: `net8.0;net10.0` (multi-target)
+- Added `Exe` (required for xUnit v3)
+- `xunit` 2.4.1 → `xunit.v3` 3.2.2
+- `xunit.runner.visualstudio` 2.4.5 → 3.1.5
+- `FluentAssertions` 6.6.0 → 8.9.0
+- `Microsoft.Extensions.Configuration.Binder` 6.0.0 → 10.0.6
+- `Microsoft.NET.Test.Sdk` 17.1.0 → 18.4.0
+
+- [ ] **Step 2: Verify the test project restores**
+
+```bash
+rtk dotnet restore Tomlyn.Extensions.Configuration.Tests/Tomlyn.Extensions.Configuration.Tests.csproj
+```
+
+Expected: Restore succeeds for net8.0 and net10.0.
+
+---
+
+### Task 5: Build the full solution and run tests
+
+**Files:** None (verification only)
+
+- [ ] **Step 1: Build the full solution**
+
+```bash
+rtk dotnet build Tomlyn.Extensions.Configuration.slnx
+```
+
+Expected: Build succeeds with 0 errors. Warnings about nullable reference types are acceptable.
+
+- [ ] **Step 2: Run all tests**
+
+```bash
+rtk dotnet test Tomlyn.Extensions.Configuration.slnx
+```
+
+Expected: All tests pass on both net8.0 and net10.0 TFMs:
+- `TomlConfigurationExtensionsTest.Bind` - PASS
+- `TomlToSnakeCaseModifierTest.BindCorrect` - PASS
+
+- [ ] **Step 3: Troubleshoot if tests fail**
+
+If tests fail due to FluentAssertions 8.x API changes, check the error message. Common changes:
+- `BeEquivalentTo` options API may have changed
+- Some assertion methods may have been renamed
+
+If tests fail due to Tomlyn v2 type changes, check whether `TomlTableArray` is still used or if arrays of tables are now represented differently.
+
+---
+
+### Task 6: Commit all changes
+
+- [ ] **Step 1: Stage and commit**
+
+```bash
+rtk git add Tomlyn.Extensions.Configuration.slnx Tomlyn.Extensions.Configuration/Tomlyn.Extensions.Configuration.csproj Tomlyn.Extensions.Configuration/TomlConfigurationFileParser.cs Tomlyn.Extensions.Configuration.Tests/Tomlyn.Extensions.Configuration.Tests.csproj
+rtk git rm Tomlyn.Extensions.Configuration.sln
+rtk git commit -m "Modernize solution: slnx, net8.0/net10.0, Tomlyn v2.3.0, xUnit v3
+
+- Convert .sln to .slnx format
+- Target netstandard2.0, net8.0, net10.0 (drop net461)
+- Upgrade Tomlyn from 0.14.3 to 2.3.0
+- Migrate Toml.Parse().ToModel() to Toml.ToModel()
+- Upgrade test framework to xUnit v3 (3.2.2)
+- Update all NuGet dependencies to latest stable
+- Bump package version to 2.0.0"
+```
diff --git a/docs/superpowers/specs/2026-04-16-modernization-design.md b/docs/superpowers/specs/2026-04-16-modernization-design.md
new file mode 100644
index 0000000..cf1ac97
--- /dev/null
+++ b/docs/superpowers/specs/2026-04-16-modernization-design.md
@@ -0,0 +1,92 @@
+# Tomlyn.Extensions.Configuration Modernization Design
+
+## Summary
+
+Modernize the Tomlyn.Extensions.Configuration project to support .NET 8/10, upgrade to Tomlyn v2.3.0, convert to .slnx format, and bump package version to 2.0.0.
+
+## Scope
+
+### In Scope
+
+1. Solution file: `.sln` -> `.slnx`
+2. Target frameworks: remove `net461`, keep `netstandard2.0`, add `net8.0` and `net10.0`
+3. Tomlyn dependency: `0.14.3` -> `2.3.0`
+4. Microsoft.Extensions dependencies: upgrade to latest stable
+5. Test project: update to `net8.0;net10.0` and latest test dependencies
+6. Package version: `1.0.6` -> `2.0.0`
+
+### Out of Scope
+
+- Directory.Build.props / Directory.Packages.props (Central Package Management)
+- global.json
+- CI/CD configuration
+- Public API surface changes (extensions, sources, providers)
+- TomlToSnakeCaseModifier logic (unaffected by Tomlyn v2)
+
+## Design Details
+
+### 1. Solution File (.slnx)
+
+Delete `Tomlyn.Extensions.Configuration.sln` and create `Tomlyn.Extensions.Configuration.slnx`:
+
+```xml
+
+
+
+
+```
+
+### 2. Main Project (.csproj)
+
+| Property | Old | New |
+|----------|-----|-----|
+| TargetFrameworks | `netstandard2.0;net461` | `netstandard2.0;net8.0;net10.0` |
+| PackageVersion | `1.0.6` | `2.0.0` |
+| Tomlyn | `0.14.3` | `2.3.0` |
+| M.E.Configuration.FileExtensions | `5.0.0` | latest stable |
+
+Clean up deprecated properties:
+- Remove `PackageIconUrl` (deprecated in modern NuGet)
+
+### 3. Tomlyn v2 API Migration
+
+Single code change in `TomlConfigurationFileParser.cs`:
+
+```csharp
+// Old (v0.14.3):
+var doc = Toml.Parse(reader.ReadToEnd());
+VisitObject(doc.ToModel());
+
+// New (v2.3.0):
+var model = TomlSerializer.Deserialize(reader.ReadToEnd());
+VisitObject(model);
+```
+
+Rationale:
+- `Toml` static facade was removed in Tomlyn v1.0, replaced by `TomlSerializer`
+- `TomlSerializer.Deserialize(string)` returns `TomlTable`, throwing `TomlException` on errors
+- `TomlTable`, `TomlArray`, `TomlTableArray` types still exist in v2 - visitor logic unchanged
+- `TomlConfigurationProvider.Load()` already wraps exceptions as `FormatException`
+
+### 4. Test Project (.csproj)
+
+| Property | Old | New |
+|----------|-----|-----|
+| TargetFrameworks | `net6.0` | `net8.0;net10.0` |
+| FluentAssertions | `6.6.0` | latest stable |
+| M.E.Configuration.Binder | `6.0.0` | latest stable |
+| xunit | `2.4.1` | remove, replaced by `xunit.v3` (latest v3) |
+| xunit.runner.visualstudio | `2.4.5` | remove, replaced by `xunit.v3.runner.visualstudio` (latest v3) |
+| Microsoft.NET.Test.Sdk | `17.1.0` | latest stable |
+
+xUnit v3 migration notes:
+- Package names change: `xunit` -> `xunit.v3`, `xunit.runner.visualstudio` -> `xunit.v3.runner.visualstudio`
+- `[Fact]` and `[Theory]` attributes remain compatible
+- Test code may need minor namespace adjustments
+
+### 5. Risk Assessment
+
+- **Low risk**: Framework target changes - netstandard2.0 remains the baseline
+- **Low risk**: Tomlyn API change - only one call site, well-documented migration
+- **Low risk**: .slnx format - straightforward XML, supported since VS 17.10/.NET 9
+- **Medium risk**: Tomlyn v2 may change how certain TOML types are represented - tests will validate