eliminate-redundant-restores
Skill1.1k starsChanged 23 days agoMITView on GitHub
Remove repeated NuGet restores from multi-step .NET builds safely. USE FOR: build/test/publish steps that each restore, Directory.Build.rsp with -restore, safe restore suppression, NETSDK1004/NETSDK1047 after --no-restore. DO NOT USE FOR: slow first restore, only one restore, version or lock errors, unranked build slowness (use build-perf-baseline).
We split this skill into the rules it gives an agent
4 rules. About 0 are common, 2 are distinctive, and 2 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈2
Distinctive · estimate
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 60
Keep the first restore. Skip a later one only when all four checks pass:
- Very specific: line 73
Keep self-contained publish settings.
- Very specific: line 75
Never remove the first restore.
- Distinctive: line 76
Do not bundle static graph restore or package caching into this change.
≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Eliminate Redundant Restores
A no-op restore still evaluates every project and checks restore state, so each extra restore in a multi-step build costs seconds to tens of seconds in a large repo. The cure is easy to get wrong: --no-restore on a step that no earlier restore actually covered turns a slow build into a failed one. This skill finds which invocations restore, proves which are already covered, and changes only those.
When to Use
- A pipeline or script runs several
dotnet/msbuildsteps and the log shows restore output in more than one of them - A restore step exists, yet later steps still print
Determining projects to restore... - A repository
Directory.Build.rspor the active MSBuild installation'sMSBuild.rspcontains-restore - Someone asks where
--no-restoreor-restore:falsecan safely be added NETSDK1004orNETSDK1047appeared after--no-restorewas added
When Not to Use
- The first restore is slow (feed latency, missing package cache): that is caching, not redundancy
- There is only one restore across all invocations: nothing to deduplicate
- Restore errors about versions, lock files or auditing: fix the restore itself
- Unranked "why is my build slow": start with
build-perf-baseline, thenbuild-perf-diagnostics
Inputs
Discover repository inputs in the working directory; do not ask the user for paths.
- CI definitions (
*.yml,.github/workflows,azure-pipelines*.yml,Jenkinsfile) and build scripts (build.cmd,build.sh,build.ps1,eng/) Directory.Build.rsp,Directory.Build.props/.targets,NuGet.config- Auto-loaded
MSBuild.rspbeside the active MSBuild executable, or response files passed explicitly with@file. A repository-localMSBuild.rspis not auto-loaded - Restore evidence if present: step logs or a binlog
Workflow
Step 1: Confirm restore is repeated and worth removing
- Log evidence:
Determining projects to restore...orAll projects are up-to-date for restore.once per invocation. - Binlog evidence: restore evaluations carry the global properties
MSBuildRestoreSessionIdandExcludeRestorePackageImports=true; the Target Performance Summary listsRestorewith N calls. - Quantify each no-op restore. If there is one restore, or the repeated ones total about a second, report "nothing worth removing" and stop without editing.
Step 2: Inventory every invocation in execution order
| Invocation | Restores? |
dotnet build, test, publish, pack | Implicitly, unless --no-restore; also check inherited rsp flags |
dotnet run | Implicitly before building; --no-restore can still inherit rsp restore on older SDKs. With --no-build, no build or restore runs |
dotnet test, publish, pack with --no-build | No implicit restore, but an rsp -restore can still enable restore; add --no-restore -restore:false |
dotnet msbuild, msbuild.exe, CI MSBuild tasks | With -restore or -t:Restore, including flags inherited from repository Directory.Build.rsp or installation-level MSBuild.rsp, even when a step looks build-only |
dotnet restore, nuget restore, msbuild -t:Restore | Explicit restore |
| CI build task with a "restore packages" option | Check its default |
Starting with .NET SDK 9.0.1xx, --no-restore forwards -restore:false to override rsp flags; older SDKs can still inherit -restore despite skipping the CLI's implicit restore. Whenever an rsp enables restore, use the version-independent --no-restore -restore:false with dotnet build, test, publish or pack, even with --no-build. dotnet msbuild and msbuild.exe take -restore:false, not --no-restore.
dotnet run does not expose the MSBuild -restore:false override. With an rsp on older SDKs, first build using dotnet build --no-restore -restore:false, then run using dotnet run --no-build with the same project, configuration, framework and runtime inputs (or execute the built DLL directly). Passing -restore:false to dotnet run is not a restore-suppression strategy.
Step 3: Decide per invocation
Keep the first restore. Skip a later one only when all four checks pass:
| Check | If it fails |
Same project set. A restore covers its entry project or solution and that project's ProjectReference closure. It does not cover a project in another solution or .slnf, a tool built from another path, or a project an orchestrating .proj builds through the MSBuild task (-restore on the orchestrator leaves those unrestored and they fail with NETSDK1004) | Keep restore on that step, or add the project to the explicit restore step |
Same restore inputs. -r/--runtime, RID-producing -a/--arch, --os, --ucr/--use-current-runtime, TargetFramework(s), RuntimeIdentifier(s), PublishAot, package sources (--source, --configfile, RestoreSources), RestorePackagesPath, and any -p: value a PackageReference condition reads. Architecture/OS options combine with the SDK's current RID; --use-current-runtime selects it. Missing RID targets fail with NETSDK1047 (Assets file ... doesn't have a target for 'net10.0/linux-x64') | Match the effective RID in the explicit restore (for example publish --os linux --arch x64 requires restore -r linux-x64), or keep restore on the later step. A generic restore does not cover these aliases merely because there is no -r. Do not require SelfContained=true on restore solely because publish is self-contained: RID-only restore covers ordinary SDK projects. Match Configuration or SelfContained only if repository conditions change package inputs |
Same available restore state. Earlier project.assets.json files must exist where the later invocation looks for them. Match --artifacts-path, UseArtifactsOutput/ArtifactsPath, and MSBuildProjectExtensionsPath/BaseIntermediateOutputPath; another job or workspace does not automatically share these files | Align the explicit restore's output layout with build/test, or retain restore in the later workspace. For build --artifacts-path artifacts --no-restore, use restore --artifacts-path artifacts first; ordinary obj/ assets do not cover that layout |
Nothing changes packages in between. A step that generates or edits a project, runs dotnet add package, rewrites Directory.Packages.props or NuGet.config invalidates the earlier restore | Move the explicit restore after that step |
Step 4: Apply the smallest change
- One explicit restore carrying every restore input, then
--no-restore,-restore:falseor--no-buildon later steps. - Several
dotnet teststeps over one solution: build once, thendotnet test --no-buildon each. Add--no-restore -restore:falseif an rsp enables restore, including on .NET 8 and earlier.--no-restorealone still rebuilds. - Keep self-contained publish settings. A
publish --no-buildstill needs a prior build with matching configuration, framework, RID and self-contained output; restore compatibility does not prove that build outputs exist. - Scope the change to CI and scripts. Leave
-restoreinDirectory.Build.rsp, because developers rely on it; pass-restore:falseper invocation instead. - Never remove the first restore. If it is a
--locked-modegate, keep it there. - Do not bundle static graph restore or package caching into this change.
# One restore carries every input later steps use
dotnet restore MyApp.sln -r linux-x64
# Explicit overrides also work when an rsp enables restore on older SDKs
dotnet build MyApp.sln -c Release --no-restore -restore:false
dotnet test MyApp.sln -c Release --no-build --no-restore -restore:false
dotnet publish src/Api -c Release -r linux-x64 --no-restore -restore:false
# msbuild invocations need the explicit switch if an rsp file adds -restore
dotnet msbuild Packaging.proj -t:Pack -restore:falseStep 5: Validate and report truthfully
- Run the changed pipeline or script end to end in a clean temporary copy (no
obj/, noproject.assets.json). A project that was never restored only fails there. YAML syntax or flag-presence checks do not validate restore coverage or test execution. If you cannot run it, say "not validated" and list the steps and projects to confirm. - Success evidence: every later step passed;
Restorecalls (binlog) orDetermining projects to restorelines fell from N to the expected count; for a time claim, before/after medians of at least three runs. - Report a restore failure (NETSDK1004, NETSDK1047) separately from a build failure, and never claim success after either.
Output
Explain which steps restored, which earlier restore covers them, what changed, and what remains unvalidated. State whether redundant restores were removed or no change was needed, keeping the required first restore. Use prose or a findings table as appropriate; no fixed table headings or verdict syntax are required.
Common Pitfalls
| Pitfall | Solution |
Adding --no-restore to every command | Apply the Step 3 checks per step |
Missing that an rsp adds -restore to "build-only" msbuild steps | Search for *.rsp before concluding a step does not restore |
Deleting -restore from Directory.Build.rsp | Override per CI invocation; the rsp serves developers |
Orchestrator .proj restored, children not | Restore each child explicitly, or keep restore on the step that builds it |
Publishing with -r, architecture/OS options, or --use-current-runtime after a generic restore | Restore with the same effective runtime identifier, including SDK-host defaults used by the aliases |
| A generator step edits PackageReferences after restore | Restore after the generator |
Trending
Quiet since September.about the whole repository
Who's talking about microsoft/testfx
- Stack Overflow“App.config not being loaded in .Net Core MSTests project”Stack Overflow · Feb 24, 2019 · 35 votes
- Stack Overflow“Visual Studio Test Explorer doesn't run tests after changes”Stack Overflow · Dec 21, 2022 · 31 votes
- Stack Overflow“DataTestMethod vs TestMethod”Stack Overflow · Dec 3, 2019 · 19 votes
- Stack Overflow“ConfigurationManager.AppSettings is always empty”Stack Overflow · May 5, 2020 · 17 votes
- dotnet/sdk“[Proposal]: Add Microsoft.NET.Sdk.Testing”GitHub · Jun 7, 2025 · 10 reactions
The biggest 5 of 68 mentions linking to the repository that our crawl found. Each line is their own headline.
See all 68
Talked about on GitHub and Stack Overflow (68 times).
On GitHub · 41
- Jun 7, 2025[Proposal]: Add Microsoft.NET.Sdk.Testing — dotnet/sdksays it tested it10 reactions · 15 comments
- Feb 4, 2025Dependabot job fails due to attempt to update packages referenced via an MSBuild SDK — dependabot/dependabot-core9 reactions · 13 comments
- Feb 22, 2024Microsoft Testing Platform for xUnit — xunit/visualstudio.xunitsays it tested it6 reactions · 19 comments
- Dec 3, 2018DataTestMethod vs TestMethod — microsoft/testfx-docs6 reactions · 4 comments
- Feb 19, 2024Microsoft Testing Platform for NUnit — nunit/nunit3-vs-adaptersays it tested it5 reactions · 1 comment
36 more on GitHub
- Dec 19, 2023Arcade should opt into `BuildWithNetFrameworkHostedCompiler` by default — dotnet/arcade5 reactions · 18 comments
- Nov 24, 2025Native support for Microsoft.Testing.Platform? — danielpalme/ReportGeneratorsays it tested it4 reactions · 3 comments
- Oct 31, 2025Selecting test runner without global.json — dotnet/sdk3 reactions · 2 comments
- Jul 15, 2025[Analyzer Proposal]: Don't use `stringBuilder.Append(new string(c, repeatCount))` — dotnet/runtime3 reactions · 3 comments
- Jul 13, 2025Allow `dotnet test --list-tests json` — dotnet/sdk3 reactions · 7 comments
- Jul 9, 2025Improve output of `dotnet test` with MTP — dotnet/sdk3 reactions · 6 comments
- Jan 30, 2024Support for the new test runner — nunit/nunit3 reactions · 3 comments
- Oct 17, 2017[ExcludeFromCodeCoverageAttribute] should be applicable to assemblies — dotnet/runtime3 reactions · 12 comments
- Sep 2, 2026chore(deps): update mstest to 4.4.0 — thomhurst/TUnit2 reactions · 3 comments
- Dec 17, 2025[MTP] Cannot switch off terminal logger when building during dotnet test — dotnet/sdk2 reactions
- Oct 8, 2025Bump MSTest.TestFramework from 3.11.0 to 4.0.0 — files-community/Files2 reactions · 1 comment
- May 27, 2025Dependabot Ignores Configuration File Settings in latest Version — dependabot/dependabot-core2 reactions · 7 comments
- May 20, 2025Add `-vs` and `-vscode` to build scripts — dotnet/arcade2 reactions · 8 comments
- Mar 14, 2025Add template for dotnet.config to dotnet new — dotnet/sdk2 reactions · 2 comments
- Jan 16, 2024An Allure adapter for MSTest — allure-framework/allure-csharp2 reactions · 2 comments
- Apr 10, 2018Added docs for dynamic data display name — microsoft/testfx-docs2 reactions · 1 comment
- Sep 3, 2026chore(deps): update microsoft.testing — thomhurst/TUnit1 reaction · 3 comments
- Mar 16, 2026Bump dotnet-sdk from 10.0.103 to 10.0.201 — jas88/FAnsiSql1 reaction
- Mar 16, 2026Bump dotnet-sdk from 10.0.103 to 10.0.201 — jas88/libarchive.net1 reaction
- Mar 16, 2026Bump dotnet-sdk from 10.0.103 to 10.0.201 — jas88/RDMP1 reaction
- Mar 16, 2026sdk(deps): Bump dotnet-sdk from 10.0.103 to 10.0.201 — jas88/CsvHandler1 reaction · 1 comment
- Mar 16, 2026chore(deps): bump dotnet-sdk from 10.0.103 to 10.0.201 — jas88/SharpDicom1 reaction
- Mar 16, 2026Bump dotnet-sdk from 10.0.103 to 10.0.201 — jas88/nccid1 reaction
- Mar 15, 2026chore(deps): bump dotnet-sdk from 10.0.103 to 10.0.201 — jas88/filestats1 reaction
- Mar 15, 2026Bump dotnet-sdk from 10.0.103 to 10.0.201 — jas88/MCPsharp1 reaction
- Mar 15, 2026Bump dotnet-sdk from 10.0.103 to 10.0.201 — jas88/SynthEHR1 reaction
- Mar 15, 2026Bump dotnet-sdk from 10.0.103 to 10.0.201 — jas88/TypeGuesser1 reaction
- Mar 15, 2026Bump dotnet-sdk from 10.0.103 to 10.0.201 — jas88/LineReader1 reaction
- Mar 15, 2026Bump dotnet-sdk from 10.0.103 to 10.0.201 — jas88/DicomTypeTranslation1 reaction
- Mar 12, 2026Bump dotnet-sdk from 10.0.103 to 10.0.201 — jas88/dcm2csv1 reaction
- Mar 11, 2026chore(deps): bump dotnet-sdk from 10.0.103 to 10.0.200 — jas88/PACSify1 reaction
- Mar 7, 2025Use Microsoft Testing Platform — martincostello/alexa-london-travelsays it tested it1 reaction · 3 comments
- Feb 17, 2025Bump the mstest group across 1 directory with 2 updates — kusl/fluentassertions1 reaction · 9 comments
- May 25, 2026Bump MSTest.TestAdapter and MSTest.TestFramework — fluentassertions/fluentassertions0 reactions · 8 comments
- May 18, 2026Bump the mstest group with 2 updates — fluentassertions/fluentassertions0 reactions · 3 comments
- May 11, 2026Bump MSTest.TestAdapter and MSTest.TestFramework — fluentassertions/fluentassertions0 reactions · 4 comments
On Stack Overflow · 27
- Feb 24, 2019App.config not being loaded in .Net Core MSTests project35 votes
- Dec 21, 2022Visual Studio Test Explorer doesn't run tests after changes31 votes
- Dec 3, 2019DataTestMethod vs TestMethod19 votes
- May 5, 2020ConfigurationManager.AppSettings is always empty17 votes
- Dec 4, 2020The type or namespace name 'PrivateObject' could not be found6 votes
22 more on Stack Overflow
- Nov 2, 2018'TestContext' does not contain a definition for 'DataRow'5 votes
- Jun 15, 2019Get TestResult in MSTest TestCleanup in C#4 votes
- Nov 14, 2024Does a [TestMethod] have to be public? What consequences are there if it isn't?3 votes
- Aug 26, 2020'Inconclusive' tests are reported as 'not run' by Azure DevOps3 votes
- Sep 18, 2018Guidance for running tests using MSTest v1 in Visual Studio 2017 15.8.13 votes
- Nov 29, 2017Long running unit test marked as passed fails TFS build - Object 'xxx.rem' has been disconnected or does not exist at the server.**3 votes
- Jun 25, 2021use PrivateObject in .net 5 from nugget MSTest.TestFramework2 votes
- Jan 7, 2020MS Test V2 - ClassInitialize behavior when fail2 votes
- Oct 30, 2019Test Methods and Test Class in compiled DLL2 votes
- Jan 15, 2019Use configuration file to specify connection string to define a data source in unit test2 votes
- May 2, 2017Where is MSTest V22 votes
- Oct 15, 2022How to unit test private method in .Net Core application which does not support "PrivateObject"1 vote
- Oct 5, 2022Only first DataRow gets executed in MSTest1 vote
- Feb 4, 2021MS Tests DataTestMethod Timeout for a single test1 vote
- Feb 13, 2020PrivateObject class not found in .Net Core 2.01 vote
- Jan 4, 2020MsTest TestCleanup method not called when an unhandled exception is thrown1 vote
- May 3, 2019Where do I find TestContext.AddResultFile()?1 vote
- Apr 24, 2017Inherited test class from generic base is ignored in MSTest1 vote
- Aug 20, 2025Use MSTest.Sdk for TestHelper0 votes · 1 answer
- Mar 9, 2022Test results are published correctly but not reflected in pipeline Results - Azure devops (Yaml)0 votes
- Dec 22, 2021Testing with empty string using DataRow in MsTestV20 votes
- Feb 6, 2018Why does Visual Studio Always pull in the latest version Unit Test Framework within a solution when older versions are already included0 votes
In this repository62 other files it gives its agents.
- AGENTS.mdAGENTS.md
- eng/common/AGENTS.mdAGENTS.md
- .github/copilot-instructions.mdCopilot instructions
- assertion-qualitySkill
- binlog-failure-analysisSkill
- binlog-generationSkill
- build-parallelismSkill
- build-perf-baselineSkill
- build-perf-diagnosticsSkill
- check-bin-obj-clashSkill
- code-testing-agentSkill
- code-testing-extensionsSkill
- code-testingSkill
- copy-to-output-directorySkill
- coverage-analysisSkill
- crap-scoreSkill
- csharp-expertSkill
- csharp-refactoringSkill
- curation-exceptionsSkill
- detect-static-dependenciesSkill
- directory-build-organizationSkill
- eval-performanceSkill
- extension-pointsSkill
- filter-syntaxSkill
- find-untested-sourcesSkill
- generate-testability-wrappersSkill
- grade-testsSkill
- including-generated-filesSkill
- incremental-buildSkill
- item-managementSkill
- migrate-static-to-wrapperSkill
- msbuild-antipatternsSkill
- msbuild-modernizationSkill
- msbuild-serverSkill
- msbuildSkill
- mtp-hot-reloadSkill
- platform-detectionSkill
- property-patternsSkill
- resolve-project-referencesSkill
- run-testsSkill
- scaffold-dotnet-test-projectSkill
- setup-local-sdkSkill
- target-authoringSkill
- testability-obstacleSkill
- test-analysis-extensionsSkill
- test-anti-patternsSkill
- test-gap-analysisSkill
- test-smell-detectionSkill
- test-taggingSkill
- test-value-evidenceSkill
- troubleshoot-binlog-mcpSkill
- writing-mstest-testsSkill
- agentic-workflowsSkill
- behavior-preserving-refactorSkill
- code-reviewSkill
- feature-deliverySkill
- post-release-activitiesSkill
- session-delivery-hygieneSkill
- testfx-acceptance-validationSkill
- testfx-localization-validationSkill
- The first 60 shown.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

