From framework-versioning
Perform the .NET major version bump (e.g., net11 to net12) in any dotnet repo. Use when asked to "update TFMs", "create workload manifest for new version", "update from netN to netN+1", or "create frozen manifest". Covers eng/Versions.props, Directory.Build.props, workload manifests, templates, test assets, and documentation.
npx claudepluginhub lewing/agent-plugins --plugin framework-versioningThis skill uses the workspace's default tool permissions.
Orchestrate the multi-phase process of bumping any dotnet repo from .NET N to .NET N+1. The core patterns (version properties, TFMs, workload manifests) are universal across dotnet repos; repo-specific content (templates, test assets, build configs) is discovered dynamically.
Assesses .NET projects for upgrade to .NET 8, plans sequencing, modernizes code and dependencies, and updates CI/CD pipelines.
Converts .NET projects and solutions to NuGet Central Package Management (CPM) using Directory.Packages.props. Centralizes versions, resolves conflicts, syncs updates, and validates builds.
Upgrades .NET projects to target version: installs SDK, updates TFM/global.json/LangVersion, upgrades packages, builds with auto-fixes, verifies tests.
Share bugs, ideas, or general feedback.
Orchestrate the multi-phase process of bumping any dotnet repo from .NET N to .NET N+1. The core patterns (version properties, TFMs, workload manifests) are universal across dotnet repos; repo-specific content (templates, test assets, build configs) is discovered dynamically.
net11 → net12, net12 → net13)TargetFramework references across a repository after a major version changeCreate this table at the start of every run:
CREATE TABLE IF NOT EXISTS bump_files (
path TEXT PRIMARY KEY,
phase TEXT NOT NULL, -- 'core-props', 'workloads', 'projects', 'testing', 'docs', 'build-config'
category TEXT, -- e.g., 'frozen-manifest', 'template', 'test-asset', 'tfm-ref'
old_value TEXT, -- the value being replaced (e.g., 'net11.0')
new_value TEXT, -- the replacement value (e.g., 'net12.0')
status TEXT DEFAULT 'pending', -- 'pending', 'updated', 'skipped', 'verified'
notes TEXT -- why skipped, or what was changed
);
Workflow:
INSERT every discovered file with status='pending'UPDATE bump_files SET status='updated' WHERE path='...'UPDATE bump_files SET status='skipped', notes='intentional previous-version ref' WHERE path='...'SELECT * FROM bump_files WHERE status='pending' to find anything missedSELECT phase, status, COUNT(*) FROM bump_files GROUP BY phase, status at any timeAsk the user for:
eng/Versions.props <MajorVersion> if not provided.Before making changes, understand what this repo contains. Run these searches and note what exists:
# Core properties (all repos)
grep -l "MajorVersion" eng/Versions.props
grep -l "NetCoreAppCurrentVersion" Directory.Build.props
# Workload manifests (if present)
find . -type d -name "*.Manifest" | head -20
# Manifest registration — explicit list vs wildcard?
find . -name "manifest-packages.*" -exec cat {} \;
# Templates, test assets, docs (repo-specific)
find . -type f -name "template.json" -path "*/.template.config/*" | head -10
git grep -l "TargetFramework.*netN\.0" -- "**/*.csproj" | head -20
This determines which phases apply. A small repo may only need Phase 1 + 6. A complex repo like dotnet/runtime needs all phases.
Insert every discovered file into SQL immediately:
-- Example: core props files found
INSERT OR IGNORE INTO bump_files (path, phase, category) VALUES
('eng/Versions.props', 'core-props', 'version-props'),
('Directory.Build.props', 'core-props', 'tfm-props');
-- Example: workload manifests found
INSERT OR IGNORE INTO bump_files (path, phase, category) VALUES
('src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Current.Manifest/...', 'workloads', 'current-manifest');
-- Example: project files from git grep
INSERT OR IGNORE INTO bump_files (path, phase, category) VALUES
('src/mono/wasm/testassets/BlazorBasicTestApp/BlazorBasicTestApp.csproj', 'projects', 'test-asset');
Continue inserting files discovered in later phases — not all files are found in Phase 0.
Update the primary version numbers that the entire build system depends on.
Key files (if they exist):
eng/Versions.props — <MajorVersion>, <ProductVersion>, SDK band versions (e.g., 110100 → 120100), workload manifest version propertiesDirectory.Build.props — <NetCoreAppCurrentVersion>, <NetCoreAppPrevious>, <NetCoreAppMinimum>, <ApiCompatNetCoreAppBaseline*>❌ Never modify
eng/Version.Details.xml— it is auto-managed by Arcade/Maestro dependency flow.
❌ Not all version N references should change. Some intentionally refer to the previous version for compatibility or baseline testing.
⚠️ Don't set
NetCoreAppPrevioustoo early. It may be intentionally cleared during early development.
📖 See references/version-bump-instructions.md sections 1-3 for property details.
Skip if no workload manifests found in Phase 0.
Create a frozen workload manifest for netN (the previous Current version) and update workload references.
❌ The frozen manifest is for netN, NOT netN+1. When bumping to .NET 12, you create the net11 frozen manifest.
Dynamic discovery steps:
*.Current.Manifest directories — each manifest family needs a frozen versionnetN-1.Manifest in each family — use as template.pkgproj, .proj, .csproj)manifest-packages.* uses explicit ProjectReference entries, add the new one; if it uses a wildcard glob, no registration neededlocalize/ in netN-1.Manifest — if present, copy and update version refsFor each manifest family:
{Family}.netN.Manifest/ directorynetN-1.Manifest, update package name and version propertiesWorkloadManifest.json.in — copy from Current.Manifest, apply netN transformationsWorkloadManifest.targets.in — only the TFM-specific section, not the shared logiclocalize/ from netN-1.Manifest if present, update version refsRuntimeVersionNetN / version variable for the newly-frozen version❌ Do NOT blindly copy the entire shared/general section into the frozen manifest's .targets.in. Start with the TFM-specific conditional logic (after the
<!-- start of TFM specific logic -->comment in Current.Manifest), then scan the shared section for items that reference current-version properties — those may need TFM-conditioned equivalents in the frozen manifest.
⚠️ Always diff the new frozen manifest against
netN-1.Manifest— structure should be similar, with version numbers changed. New content from Current.Manifest's shared section may legitimately expand the frozen manifest beyond what netN-1 had.
Gate: Frozen manifest has same file set as netN-1.Manifest.
📖 See references/workload-version-bump-instructions.md for transformation rules and examples. 📖 See references/workload-manifest-patterns.md for structural patterns across repos.
Discover dynamically — search for files referencing the old version:
git grep -l "netN\.0" -- "**/*.csproj" "**/*.fsproj" "**/*.vbproj"
git grep -l "netN\.0" -- "**/.template.config/template.json"
git grep -l "ProductVersion.*N\.0" -- "**/package.json"
Update TFM references, template identities/choices/defaults, and PackageId values.
📖 See references/version-bump-instructions.md sections 6-8 for common patterns.
Discover dynamically — search for test-related version references:
git grep -rn "TargetMajorVersion.*=.*N" -- "**/*Test*.cs" "**/*BuildTestBase*"
git grep -l "RUNTIME_PACK_VER" -- "**/*.csproj" "**/*.targets"
git grep -l "workloads-.*\.targets" -- "eng/testing/"
Update constants, env vars, and workload testing configs found.
📖 See references/workload-version-bump-instructions.md "Testing Infrastructure Updates".
git grep -rl "netN\.0\|\.NET N\b" -- "docs/" "**/*.md" | head -30
Update version references in all documentation files found.
💡 Keep the feedback loop tight. Run the fastest check first, fix, repeat.
-- Anything still pending?
SELECT path, phase, category FROM bump_files WHERE status = 'pending';
-- Progress dashboard
SELECT phase, status, COUNT(*) as count FROM bump_files GROUP BY phase, status ORDER BY phase, status;
If pending files remain, update them or mark as skipped with a reason.
⚠️ Always substitute actual version numbers. Never run
netN.0literally.
git grep -i "netN\.0" -- ':!*.md' # Remaining TFM refs
git grep "N0100" -- '*.props' '*.targets' # SDK band versions
git grep "TargetsNetN" -- '*.props' '*.targets' # TFM conditions
Review each match — some are intentional previous-version refs.
Any new files found here that weren't in SQL should be inserted and resolved:
INSERT OR IGNORE INTO bump_files (path, phase, category, notes) VALUES
('...', 'build-config', 'tfm-ref', 'found in 6b sweep');
Diff each new frozen manifest against netN-1.Manifest. Only version numbers should differ.
Fix errors at each level before moving to the next:
| Level | Command | What it catches |
|---|---|---|
| 1 | ./build.sh -restore (or dotnet restore) | MSBuild parse errors, missing imports |
| 2 | Manifest-specific build target (if exists) | Workload manifest packaging |
| 3 | Full subset build | Version property propagation |
⚠️ Level 1 must pass before attempting Level 2.
Search for the previous version bump PR in the same repo for any missed files.
-- Confirm everything is resolved
SELECT COUNT(*) as remaining FROM bump_files WHERE status = 'pending';
-- Should return 0
eng/Versions.props <MajorVersion> updated to N+1Directory.Build.props <NetCoreAppCurrentVersion> updated to N+1.0 (if present)netN-1.Manifestbump_files table are updated, skipped, or verified — none pending