diff --git a/.fallout/build.schema.json b/.fallout/build.schema.json
index 47c6a4f95..a810b8e0e 100644
--- a/.fallout/build.schema.json
+++ b/.fallout/build.schema.json
@@ -24,24 +24,7 @@
"ExecutableTarget": {
"type": "string",
"enum": [
- "CheckoutExternalRepositories",
- "Clean",
- "Compile",
- "CreateGitHubRelease",
- "DeletePackages",
- "DownloadLicenses",
- "GeneratePublicApi",
- "GenerateTools",
- "Install",
- "Pack",
- "Publish",
- "References",
- "ReportCoverage",
- "Restore",
- "RunTargetInDockerImageTest",
- "Test",
- "UpdateContributors",
- "UpdateStargazers"
+ "Default"
]
},
"Verbosity": {
@@ -122,42 +105,9 @@
"allOf": [
{
"properties": {
- "CodecovToken": {
- "type": "string",
- "default": "Secrets must be entered via 'nuke :secrets [profile]'"
- },
- "Configuration": {
- "type": "string",
- "enum": [
- "Debug",
- "Release"
- ]
- },
- "GitHubReleaseGitHubToken": {
- "type": "string",
- "default": "Secrets must be entered via 'nuke :secrets [profile]'"
- },
- "IgnoreFailedSources": {
- "type": "boolean",
- "description": "Ignore unreachable sources during Restore"
- },
- "Major": {
- "type": "boolean"
- },
- "NuGetApiKey": {
- "type": "string",
- "default": "Secrets must be entered via 'nuke :secrets [profile]'"
- },
"Solution": {
"type": "string",
"description": "Path to a solution file that is automatically loaded"
- },
- "TestDegreeOfParallelism": {
- "type": "integer",
- "format": "int32"
- },
- "UseHttps": {
- "type": "boolean"
}
}
},
diff --git a/fallout.slnx b/fallout.slnx
index d40d8e0f9..0c05b8183 100644
--- a/fallout.slnx
+++ b/fallout.slnx
@@ -43,6 +43,9 @@
+
+
+
diff --git a/tests/Consumers/Fallout.Consumer.Local/Build.cs b/tests/Consumers/Fallout.Consumer.Local/Build.cs
new file mode 100644
index 000000000..6b97cee6e
--- /dev/null
+++ b/tests/Consumers/Fallout.Consumer.Local/Build.cs
@@ -0,0 +1,26 @@
+// Copyright 2026 Maintainers of Fallout.
+// Originally based on NUKE by Matthias Koch and contributors.
+// Distributed under the MIT License.
+// https://github.com/ChrisonSimtian/Fallout/blob/main/LICENSE
+//
+// Fallout consumer against this repo's local source (smart-rewrite makes the
+// PackageReferences resolve to ProjectReferences). Catches breakage of the
+// public Fallout surface in the current PR.
+
+using Fallout.Common;
+using Fallout.Common.IO;
+using Fallout.Solutions;
+
+class Build : FalloutBuild
+{
+ public static int Main() => Execute(x => x.Default);
+
+ [Solution] readonly Solution Solution;
+
+ Target Default => _ => _
+ .Executes(() =>
+ {
+ Serilog.Log.Information("hello from fallout consumer (local source)");
+ Serilog.Log.Information("solution name: {Name}", Solution?.Name ?? "");
+ });
+}
diff --git a/tests/Consumers/Fallout.Consumer.Local/Fallout.Consumer.Local.csproj b/tests/Consumers/Fallout.Consumer.Local/Fallout.Consumer.Local.csproj
new file mode 100644
index 000000000..0f1d18b91
--- /dev/null
+++ b/tests/Consumers/Fallout.Consumer.Local/Fallout.Consumer.Local.csproj
@@ -0,0 +1,27 @@
+
+
+
+ net10.0
+ Exe
+ false
+ Fallout.Consumer.Local
+ $(NoWarn);CS0649
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/Consumers/Fallout.Consumer.NuGet/Build.cs b/tests/Consumers/Fallout.Consumer.NuGet/Build.cs
new file mode 100644
index 000000000..d56b121c3
--- /dev/null
+++ b/tests/Consumers/Fallout.Consumer.NuGet/Build.cs
@@ -0,0 +1,27 @@
+// Copyright 2026 Maintainers of Fallout.
+// Originally based on NUKE by Matthias Koch and contributors.
+// Distributed under the MIT License.
+// https://github.com/ChrisonSimtian/Fallout/blob/main/LICENSE
+//
+// Fallout consumer against PUBLISHED Fallout.* packages (pinned in the csproj).
+// Validates that the most-recent release's surface is intact from a clean
+// consumer's perspective. Pre-#254 namespaces (Fallout.Common.ProjectModel)
+// because the pinned 11.0.8 release predates the rename.
+
+using Fallout.Common;
+using Fallout.Common.IO;
+using Fallout.Common.ProjectModel;
+
+class Build : FalloutBuild
+{
+ public static int Main() => Execute(x => x.Default);
+
+ [Solution] readonly Solution Solution;
+
+ Target Default => _ => _
+ .Executes(() =>
+ {
+ Serilog.Log.Information("hello from fallout consumer (pinned nuget 11.0.8)");
+ Serilog.Log.Information("solution name: {Name}", Solution?.Name ?? "");
+ });
+}
diff --git a/tests/Consumers/Fallout.Consumer.NuGet/Fallout.Consumer.NuGet.csproj b/tests/Consumers/Fallout.Consumer.NuGet/Fallout.Consumer.NuGet.csproj
new file mode 100644
index 000000000..3cab9aab2
--- /dev/null
+++ b/tests/Consumers/Fallout.Consumer.NuGet/Fallout.Consumer.NuGet.csproj
@@ -0,0 +1,33 @@
+
+
+
+ net10.0
+ Exe
+ false
+ Fallout.Consumer.NuGet
+ $(NoWarn);CS0649
+
+
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/Consumers/Nuke.Consumer/Build.cs b/tests/Consumers/Nuke.Consumer/Build.cs
new file mode 100644
index 000000000..21c081670
--- /dev/null
+++ b/tests/Consumers/Nuke.Consumer/Build.cs
@@ -0,0 +1,35 @@
+// Copyright 2026 Maintainers of Fallout.
+// Originally based on NUKE by Matthias Koch and contributors.
+// Distributed under the MIT License.
+// https://github.com/ChrisonSimtian/Fallout/blob/main/LICENSE
+//
+// Pre-rename NUKE consumer pattern, compiled against the Nuke.Common /
+// Nuke.Components transition shims. If a typical NUKE 10.x Build.cs stops
+// compiling against the latest Fallout, this fails — protecting upgrading
+// users from silent breakage.
+
+using Nuke.Common;
+using Nuke.Common.IO;
+using Nuke.Common.ProjectModel;
+using Nuke.Components;
+
+// The shim generator skips delegates by C# language limitation (see SHIM002 —
+// can't subclass a delegate cross-assembly). `Target` is a delegate in
+// Fallout.Common, so NUKE-era code referencing `Target` needs either
+// `fallout-migrate` (which flips usings to Fallout.*) or this manual alias.
+// Including it here keeps the rest of the file NUKE-shape.
+using Target = Fallout.Common.Target;
+
+class Build : NukeBuild
+{
+ public static int Main() => Execute(x => x.Default);
+
+ [Solution] readonly Solution Solution;
+
+ Target Default => _ => _
+ .Executes(() =>
+ {
+ Serilog.Log.Information("hello from nuke consumer (via shim)");
+ Serilog.Log.Information("solution name: {Name}", Solution?.Name ?? "");
+ });
+}
diff --git a/tests/Consumers/Nuke.Consumer/Nuke.Consumer.csproj b/tests/Consumers/Nuke.Consumer/Nuke.Consumer.csproj
new file mode 100644
index 000000000..1a5744333
--- /dev/null
+++ b/tests/Consumers/Nuke.Consumer/Nuke.Consumer.csproj
@@ -0,0 +1,18 @@
+
+
+
+ net10.0
+ Exe
+ false
+ Nuke.Consumer
+
+ $(NoWarn);CS0649
+
+
+
+
+
+
+
+
+
diff --git a/tests/Consumers/README.md b/tests/Consumers/README.md
new file mode 100644
index 000000000..4bd294765
--- /dev/null
+++ b/tests/Consumers/README.md
@@ -0,0 +1,43 @@
+# Consumer compatibility tests
+
+Three small build-project consumers that exercise Fallout's public surface from the perspectives of real downstream users. If any of these stop **compiling**, we've broken something consumers depend on.
+
+## Projects
+
+| Project | What it exercises | Reference style |
+|---|---|---|
+| [`Nuke.Consumer`](Nuke.Consumer/) | A pre-rename NUKE consumer using `class Build : NukeBuild`, `[Solution]`, and `Target` via the `Nuke.Common` / `Nuke.Build` / `Nuke.Components` transition shims. Catches breakage of **shim coverage** — if a NUKE-shape symbol stops resolving, an upgrading NUKE user breaks. | `ProjectReference` to `src/Shims/Nuke.*/` |
+| [`Fallout.Consumer.Local`](Fallout.Consumer.Local/) | A Fallout consumer using `class Build : FalloutBuild`, `[Solution]`, `Target` against **this repo's current source**. Catches breakage of the public Fallout surface **in the current PR**. | Direct `ProjectReference` to `src/Fallout.*/` and `src/Persistence/Fallout.Solution/` |
+| [`Fallout.Consumer.NuGet`](Fallout.Consumer.NuGet/) | A Fallout consumer against the **last published** `Fallout.*` packages (pinned in the csproj). Catches **packaging issues** (missing assemblies, wrong references) on the most-recent release, and catches upgrade-direction breakage when the pin is bumped after a release. | `PackageReference` to nuget.org with `false` and `false` so the smart-rewrite doesn't kick in |
+
+## How they're validated
+
+**Compile-time validation only.** All three projects are in `fallout.slnx`, so `dotnet build fallout.slnx` (the standard CI gate) compiles them. Any consumer-facing breaking change makes the build fail.
+
+### Why not runtime validation?
+
+Initial design had a `Consumers.SmokeTests/` xUnit project that spawned each consumer via `dotnet run` and asserted exit code `0`. That approach hits a Fallout framework runtime requirement: `FalloutBuild.cctor()` enumerates `Host` subclasses and reflects for a static `IsRunningHost` property — minimal consumers that don't pull in the full Fallout MSBuild props/targets (`Fallout.Common.props`, `Fallout.Common.targets`) trigger a `System.ArgumentException: Host type 'Host' defines no property 'IsRunningHost'` at activation. Reproducing the full build-app environment for a smoke consumer is meaningfully more setup than the test's value justifies; for now the consumer projects are compile-only sentinels.
+
+If we ever need runtime validation: import `Fallout.Common.props` + `Fallout.Common.targets` from the consumer csprojs (the way `build/_build.csproj` does), provide a `FalloutRootDirectory`, and the framework should activate cleanly. Not done in this initial pass because the compile-time check already catches the bulk of breaking changes.
+
+## Bumping the NuGet pin
+
+After a new `Fallout.*` release ships, edit `Fallout.Consumer.NuGet/Fallout.Consumer.NuGet.csproj` to bump the pinned version. If the consumer source still compiles, the upgrade is non-breaking. If it fails, the new release introduces a consumer-facing breaking change — record it in `CHANGELOG.md` and the migration path under `[Unreleased] — `.
+
+The `Nuke.Consumer` doesn't pin anything (it references the in-repo shim assemblies directly), so no bump cadence there — it always tracks HEAD's shim coverage.
+
+## What's deliberately tested
+
+- **NUKE-era `class Build : NukeBuild, IPack`** — basic class identity through the shim
+- **`[Solution] readonly Solution Solution`** — value-injection attribute + facade type via shim
+- **`Target Default => _ => _.Executes(...)`** — delegate type, default target lambda
+- **`static int Main() => Execute(x => x.Default)`** — framework entry point
+
+## What's NOT tested
+
+- Actual build *execution* (see above — runtime activation is fragile)
+- CI-host integration (GitHubActions, AzurePipelines, etc.) — covered by `tests/Fallout.Common.Tests/CI/`
+- Tool wrappers — covered by their own generated tests
+- Source generator behaviour — covered by `tests/Fallout.SourceGenerators.Tests/`
+
+These consumer projects are a **sentinel for shape changes**, not a place to demo features. Don't add consumer projects covering specific subsystems — those go in their own focused tests.
diff --git a/tests/Fallout.SourceGenerators.Tests/StronglyTypedSolutionGeneratorTest.Test#Solution.g.verified.cs b/tests/Fallout.SourceGenerators.Tests/StronglyTypedSolutionGeneratorTest.Test#Solution.g.verified.cs
index a1dc56a0d..06eae0c4d 100644
--- a/tests/Fallout.SourceGenerators.Tests/StronglyTypedSolutionGeneratorTest.Test#Solution.g.verified.cs
+++ b/tests/Fallout.SourceGenerators.Tests/StronglyTypedSolutionGeneratorTest.Test#Solution.g.verified.cs
@@ -17,6 +17,8 @@ internal class Solution(SolutionModel model, AbsolutePath path) : Fallout.Soluti
public Fallout.Solutions.Project Fallout_Common => this.GetProject("Fallout.Common");
public Fallout.Solutions.Project Fallout_Common_Tests => this.GetProject("Fallout.Common.Tests");
public Fallout.Solutions.Project Fallout_Components => this.GetProject("Fallout.Components");
+ public Fallout.Solutions.Project Fallout_Consumer_Local => this.GetProject("Fallout.Consumer.Local");
+ public Fallout.Solutions.Project Fallout_Consumer_NuGet => this.GetProject("Fallout.Consumer.NuGet");
public Fallout.Solutions.Project Fallout_Migrate => this.GetProject("Fallout.Migrate");
public Fallout.Solutions.Project Fallout_Migrate_Analyzers => this.GetProject("Fallout.Migrate.Analyzers");
public Fallout.Solutions.Project Fallout_Migrate_Analyzers_Tests => this.GetProject("Fallout.Migrate.Analyzers.Tests");
@@ -44,6 +46,7 @@ internal class Solution(SolutionModel model, AbsolutePath path) : Fallout.Soluti
public Fallout.Solutions.Project Nuke_Common_Shim_Tests => this.GetProject("Nuke.Common.Shim.Tests");
public Fallout.Solutions.Project Nuke_Components => this.GetProject("Nuke.Components");
public Fallout.Solutions.Project Nuke_Components_Shim_Tests => this.GetProject("Nuke.Components.Shim.Tests");
+ public Fallout.Solutions.Project Nuke_Consumer => this.GetProject("Nuke.Consumer");
public _misc misc => Unsafe.As<_misc>(this.GetSolutionFolder("misc"));