Fallout.Persistence.Solution
11.0.18
Prefix Reserved
dotnet add package Fallout.Persistence.Solution --version 11.0.18
NuGet\Install-Package Fallout.Persistence.Solution -Version 11.0.18
<PackageReference Include="Fallout.Persistence.Solution" Version="11.0.18" />
<PackageVersion Include="Fallout.Persistence.Solution" Version="11.0.18" />
<PackageReference Include="Fallout.Persistence.Solution" />
paket add Fallout.Persistence.Solution --version 11.0.18
#r "nuget: Fallout.Persistence.Solution, 11.0.18"
#:package Fallout.Persistence.Solution@11.0.18
#addin nuget:?package=Fallout.Persistence.Solution&version=11.0.18
#tool nuget:?package=Fallout.Persistence.Solution&version=11.0.18
<p align="center"> <img width="320" src=".assets/fallout-logo.svg" alt="Fallout β .NET build system" /> </p>
<p align="center"> <strong>π Documentation: <a href="https://docs.fallout.build/">docs.fallout.build</a></strong> </p>
π¦ Fallout is the successor to NUKE. Migrating from NUKE β
Fallout
Build automation for C#/.NET β the hard-fork successor to NUKE.
Rebrand in progress + v11/v12 roadmap published. This repository is being renamed from NUKE to Fallout as part of a hard fork. URLs, package names, and namespaces are migrating in stages.
What's next: v11 finishes the rebrand and lays the internal foundation for a plugin architecture; v12 ships the public Fallout.Plugin.Sdk. The full plan is in docs/roadmap.md. Five RFCs are open now to shape the SDK β your input matters most before v12 firms up.
Track v11 in milestone #6 and v12 in milestone #7.
Based on NUKE
Fallout is the successor to NUKE, originally created by Matthias Koch (@matkoch) and many contributors. Fallout continues NUKE's mission as a C#-first build automation framework for .NET β under new maintenance, with an enterprise-CI/CD focus.
The original NUKE code is preserved here under the MIT License with attribution. Major version 10.x was the last NUKE release; everything from this fork forward carries the Fallout identity.
Migrating from NUKE
If you maintain a NUKE-based build, docs/migration/from-nuke.md walks you through it. The short version:
dotnet tool install -g Fallout.Migrate
cd path/to/your-nuke-repo
fallout-migrate
Install
dotnet tool install -g Fallout.Cli
The CLI installs as fallout. Verify with fallout --help.
Upgrading from Fallout.GlobalTool? The package was renamed to Fallout.Cli for install ergonomics β same fallout command, friendlier ID. Uninstall the old one first so you don't end up with two tools claiming the same command:
dotnet tool uninstall -g Fallout.GlobalTool
For per-repo manifest pinning (.config/dotnet-tools.json), project setup, and shell completion, see the Installation guide on docs.fallout.build.
Table of Contents
Elevator Pitch
Solid and scalable CI/CD pipelines are an essential pillar for being competitive and creating a great product. But why are most of us a little afraid of touching YAML files and don't even dare to look at build scripts? Much of this is because C# developers are spoiled with a great language and smart IDEs, and they don't like missing their buddy for code-completion, ease of debugging, refactorings, and code formatting.
Fallout (NUKE's successor) brings your build automation to an even level with every other .NET project. How? It's a regular console application allowing all the OOP goodness! Besides, it solves many common problems in build automation, like parameter injection, path separator abstraction, access to solution and project models, and build step sharing across repositories. Fallout can also generate CI/CD configurations (YAML, etc.) that automatically parallelize build steps on multiple agents to optimize throughput!
Build Status
CI runs on every PR targeting main across ubuntu-latest β the only required status check. After merge to main, post-merge validation runs on windows-latest and macos-latest. Releases publish from main to nuget.org under the reserved Fallout.* prefix, via .github/workflows/release.yml. Docs-only PRs are served by a no-op companion workflow (ubuntu-latest-docs) so branch protection is satisfied without spending CI minutes on a real build.
| Workflow | Status | Trigger |
|---|---|---|
ubuntu-latest |
PR to main (code paths) β required check |
|
windows-latest |
push to main (post-merge validation) |
|
macos-latest |
push to main (post-merge validation) |
|
release |
push to main β publishes Fallout.* to nuget.org |
Multi-provider CI support (Azure Pipelines, GitLab, TeamCity, AppVeyor) was removed during the takeover and is being revived demand-driven β see #8.
Activity
Commits, issues, PRs (rolling 30 days)
Generated by Repobeats.
Stars over time
Generated by star-history.com. Auto-updates as new stargazers arrive.
Sponsorship
Fallout is volunteer-run. There's no donation channel yet, but we want to be transparent about what running the project costs β see costs.md for the full list. If you or your organisation would like to help offset those costs, open an issue and we'll work out the details.
Credits
- Matthias Koch and the NUKE contributors β for creating and maintaining NUKE through version 10.x.
If you maintained or contributed to NUKE and want to be credited differently here, please open an issue.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- System.Memory (>= 4.6.3)
- System.Threading.Tasks.Extensions (>= 4.6.3)
-
net10.0
- No dependencies.
-
net8.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
### Breaking changes
β’ **Inlined vs-solutionpersistence parser β’ renamed Fallout.SolutionModel β Fallout.Solution; namespace Fallout.Common.ProjectModel β Fallout.Solutions*β’ (closes #248). The vendored Microsoft fork (vendor/vs-solutionpersistence/, submodule, fork-of-fork-of-Microsoft) is gone β sources inlined into a new Fallout.Persistence.Solution project under src/Persistence/. The facade was renamed and rehoused alongside it (src/Persistence/Fallout.Solution/), with the long-standing rebrand-era namespace mismatch (Fallout.Common.ProjectModel) fixed to match the assembly name (Fallout.Solutions, plural per BCL convention to avoid Fallout.Solution.Solution awkwardness).
β’ **Package IDs**: Fallout.SolutionModel β Fallout.Solution, Fallout.VisualStudio.SolutionPersistence β Fallout.Persistence.Solution. Consumers that explicitly referenced either need to update their PackageReference/ProjectReference.
β’ **Namespaces**: Fallout.Common.ProjectModel β Fallout.Solutions; Microsoft.VisualStudio.SolutionPersistence.{Model,Serializer,Utilities,...} β Fallout.Persistence.Solution.{Model,Serializer,Utilities,...}. Replace using statements accordingly. The Nuke.Common.ProjectModel.* transition-shim path is preserved (ShimMarker.cs now mirrors both Fallout.Common.* β Nuke.Common.* and Fallout.Solutions.* β Nuke.Common.ProjectModel.*), so NUKE-era consumer code using using Nuke.Common.ProjectModel; [Solution] readonly Solution Solution; keeps compiling.
β’ **Onion layering**: this PR establishes src/Persistence/ as the layered home for persistence-ring code. Future persistence-related projects go under the same directory.
β’ **Visibility narrowing deferred**: parser types remain public for this PR; the IVT decorations in Fallout.Persistence.Solution.csproj are future-intent. Per-type internal narrowing will follow in a later PR (cascading CS0050 analysis needed).
β’ **Codefix follow-up**: Fallout.Migrate.Analyzers's NukeβFallout rewriter still produces Fallout.Common.ProjectModel.* rather than Fallout.Solutions.*. Tracked separately.
β’ **Consumer-compat sentinel updated**: tests/Consumers/Fallout.Consumer.Local/ (added in the prior PR to detect exactly this kind of rename) had its ProjectReference path and using statement updated in this PR β that update IS the documented consumer migration recipe. Nuke.Consumer and Fallout.Consumer.NuGet (pinned to 11.0.8) were unaffected, confirming the shim coverage and prior-release stability hold.
β’ **AES-GCM v2 secret format for EncryptionUtility β parameters.json encrypted values*β’ (#214, closes #212). The secret-encryption scheme used by fallout :secrets is now AES-GCM with per-secret random salt β’ nonce and 600,000 PBKDF2-SHA256 iterations (OWASP 2023). Previous v1: (AES-CBC, static salt, 10,000 iterations, unauthenticated) values **continue to decrypt*β’ β the Decrypt path dispatches on v1:/v2:/unprefixed-legacy. New Encrypt calls always emit v2:.
β’ **On-disk format**: v2:base64(salt[16] || nonce[12] || tag[16] || ciphertext). v1: was v1:base64(salt-as-iv || ciphertext) with a static Ivan Medvedev salt.
β’ **Migration path**: existing .fallout/parameters.json files with v1: values stay readable. Re-running fallout :secrets to add or update **any*β’ secret naturally re-encrypts that entry under v2: (existing SaveSecrets flow already calls Encrypt per value). A whole-file rekey command can be added if there's demand.
β’ **Mixed-version repos**: developers on Fallout < 11.0 pulling a parameters.json that contains v2: values from a teammate on 11.0β’ get Could not decrypt 'X' with provided password. Upgrade direction is fine (v1: reads keep working); downgrade is not.
β’ **Rfc2898DeriveBytes constructor obsoletion (SYSLIB0060)*β’ cleared β both legacy and current paths now use the static Rfc2898DeriveBytes.Pbkdf2(...). No public-API impact; just stops the warning.
β’ **Fallout.GlobalTool package renamed to Fallout.Cli*β’ (#206). The dotnet-tool install command is now dotnet tool install -g Fallout.Cli. The CLI **command name stays fallout*β’ β no script or invocation changes needed.
β’ **NuGet package ID changes**: Fallout.GlobalTool (frozen at 10.3.40, then unlisted) β Fallout.Cli (10.3.41 onward, then 11.0.x).
β’ **Project / assembly / namespace rename**: src/Fallout.GlobalTool/ β src/Fallout.Cli/, tests/Fallout.GlobalTool.Tests/ β tests/Fallout.Cli.Tests/, namespace Fallout.GlobalTool[.*] β namespace Fallout.Cli[.*] across 48 files. Internal types only β no public-API consumer breakage from the namespace rename itself.
β’ **Migration for consumers**: see [docs/migration/from-globaltool-to-cli.md](docs/migration/from-globaltool-to-cli.md). Short form: dotnet tool uninstall -g Fallout.GlobalTool && dotnet tool install -g Fallout.Cli, and in repos with a local manifest, edit .config/dotnet-tools.json to replace fallout.globaltool with fallout.cli.
β’ **Update-notification text*β’ in UpdateNotificationAttribute already points at the new name, so existing Fallout.GlobalTool installs will prompt users toward Fallout.Cli on next run.
β’ **Thin bootstrappers β’ .config/dotnet-tools.json manifest; build.cmd dropped*β’ (#204, PR-B of #203). The per-repo build.cmd/build.sh/build.ps1 shape changes substantially β and the [GitHubActions] generator now emits a different workflow shape downstream consumers will see on the next regen.
β’ **build.cmd is gone*β’ from the canonical scaffold. fallout :setup no longer emits it. New repos get only build.sh β’ build.ps1.
β’ **build.sh / build.ps1 are ~60-line thin shims**: provision dotnet (kept verbatim from the pre-existing block), dotnet tool restore, exec dotnet fallout $@. The BUILD_PROJECT_FILE / BUILD_DIRECTORY config β’ explicit dotnet build β’ dotnet run --project lines are gone β Fallout.Cli's in-tool runner (added in #201) does that work now.
β’ **.config/dotnet-tools.json*β’ is the new home of the pin. fallout :setup writes one with the tool pinned at the running CLI's own version. Skipped if one already exists (consumer may have other local tools pinned).
β’ **[GitHubActions] generator*β’ (Fallout.Common/CI/GitHubActions/Configuration/GitHubActionsRunStep.cs): the single run: ./{BuildCmdPath} {targets} step is replaced with three steps β actions/setup-dotnet@v4 (reads global.json), dotnet tool restore, dotnet fallout {targets}. Downstream consumers using [GitHubActions(InvokedTargets = ...)] see the new shape on next workflow regen.
β’ **BuildCmdPath property*β’ on GitHubActionsAttribute is no longer consumed (the run-step no longer references it). The Azure Pipelines / AppVeyor / TeamCity / SpaceAutomation generators still fall back to a literal build.cmd if BuildCmdPath is unset β keeps those legacy providers functional for the demand-driven revival (#8) without forcing this repo to keep a build.cmd.
β’ **Migration**: existing repos can keep their fat bootstrappers and they'll work indefinitely (the bootstrapper does its own dotnet build β’ dotnet run --project, doesn't depend on the global tool). To adopt the new shape, re-run fallout :setup --force (or hand-edit the shims to match the new template).
β’ **Last-mile Newtonsoft removal β closes the #83 migration tree*β’ (#119 STJ-6, #115 STJ-2 tail). Every Fallout source file is STJ-native; Newtonsoft.Json.dll survives in the closure only as a transitive of NuGet.Packaging and Serilog.Formatting.Compact.Reader. The Newtonsoft.Json PackageVersion is gone from Directory.Packages.props entirely.
β’ **GitHubActions.GitHubEvent is now JsonObject*β’ (was Newtonsoft JObject). Consumers that introspect the event payload need to swap using Newtonsoft.Json.Linq β using System.Text.Json.Nodes and update accessors ([key].Value<T>() β [key].GetValue<T>(), Property(name) β indexer, etc.).
β’ **Fallout.Utilities.Net HttpRequestExtensions.WithJsonContent / HttpResponseExtensions.GetBodyAsJson defaults are STJ.*β’ The [Obsolete]-marked Newtonsoft overloads (JsonSerializerSettings parameter, JObject return) are gone. JsonSerializerOptions overloads remain for explicit configuration; the parameterless default uses STJ with default options. GetBodyAsJsonObject returns System.Text.Json.Nodes.JsonObject.
β’ **SlackMessageActionButton.Type / TeamsMessage.Type / TeamsMessage.Context**: hand-written [JsonProperty(...)] attributes swapped to [JsonPropertyName(...)]. Consumer impact only if you subclass these types and added [JsonProperty]-style attributes β switch to [JsonPropertyName].
β’ **TwitterTasks**: internal error-parsing path swapped from JObject.Parse to JsonNode.Parse. No public-API change.
β’ **ArgumentsFromParametersFileAttribute*β’ (#115 STJ-2 tail): parameters.json reading swapped from JObject to JsonObject. Behaviour identical for valid input; STJ may be slightly stricter about malformed JSON.
β’ **SchemaUtility rewritten on System.Text.Json β NJsonSchema gone*β’ (#114, STJ-1 of #83). The build-parameter schema generator (which emits .fallout/build.schema.json for editor autocomplete) no longer goes through NJsonSchema. It now hand-rolls the draft-04 schema using JsonNode/JsonObject directly β same output shape, no Rico-entanglement.
β’ **Dropped packages**: NJsonSchema, NJsonSchema.NewtonsoftJson, NJsonSchema.Annotations, Namotion.Reflection β four packages and ~12 transitive dependencies gone from Fallout.Build. Newtonsoft.Json stays in the closure only via Fallout.Common's *Tasks.cs (Slack/Twitter/Teams) until #119 lands.
β’ **API**: SchemaUtility.GetJsonString(IFalloutBuild) and GetJsonDocument(IFalloutBuild) unchanged for consumers.
β’ **Behavior preserved**: well-known definitions in canonical order (Host, ExecutableTarget, Verbosity, FalloutBuild), allOf:[user, base] envelope, [CustomParameter] subclasses render as plain string (not recursive), value-type Nullable<T> β [T, null], reference-type nullables β [null, T] for primitives / oneOf [null, $ref] for refs, [TypeConverter]-attributed types (AbsolutePath, Solution, etc.) render as string.
β’ **Fallout.Tooling engine migrated to System.Text.Json*β’ (#117 STJ-4, #118 STJ-5). The tool-options β JSON layer that powers every tool wrapper is now STJ-native:
β’ Options.InternalOptions is now System.Text.Json.Nodes.JsonObject (was Newtonsoft JObject). Options.JsonSerializer and Options.JsonSerializerSettings are gone β use Options.SerializerOptions (JsonSerializerOptions) instead.
β’ The OptionsβInternalOptions converter is now a JsonConverterFactory registered on SerializerOptions.Converters (STJ doesn't inherit class-level [JsonConverter] attributes onto subclasses). LookupTable<,> round-trips through a parallel ObjectFromFieldConverter factory; Enumeration subclasses go through a new EnumerationJsonConverterFactory that bridges [TypeConverter]-attributed types (which STJ doesn't honour natively).
β’ All 62 generated tool wrappers regenerated with [JsonPropertyName] instead of [JsonProperty] (#118). The generator's own JSON model (Fallout.Tooling.Generator) is migrated too β Tool / Property / DataClass / Enumeration / Task use [JsonRequired] / [JsonPropertyName] / [JsonIgnore] from System.Text.Json.Serialization.
β’ IReadOnlyDictionary<string, object> round-trips deserialize values as JsonElement (not string as Newtonsoft did); DelegateHelper.ParseCollection coerces both shapes back to string.
β’ **Newtonsoft.Json PackageReference removed*β’ from Fallout.Tooling, Fallout.Tooling.Generator, and Fallout.Utilities.Text.Json. Object.ToJObject deleted (closes the residual #116 tail). Fallout.Common still references Newtonsoft for *Tasks.cs files until #119 (STJ-6) lands.
β’ Migration for consumers: replace Options.JsonSerializerSettings β Options.SerializerOptions; replace obj.ToJObject(serializer) β JsonSerializer.SerializeToNode(obj, Options.SerializerOptions).AsObject(); tool wrappers using hand-written [JsonProperty] (e.g. SlackMessage.Type) need to switch to [JsonPropertyName].
β’ **Fallout.Utilities.Text.Json Newtonsoft surface removed*β’ (#116, STJ-3 of #83). The [Obsolete]-marked Newtonsoft.Json overloads added in 10.3.x are gone:
β’ **Deleted types:*β’ JObjectExtensions (GetChildren/GetPropertyValue/GetPropertyStringValue/GetPropertyValueOrNull on JObject), AllWritableContractResolver, Base64JsonConverter<T>, Object.ToJObject.
β’ **JsonExtensions stripped to System.Text.Json only**: DefaultSerializerSettings removed (use DefaultSerializerOptions); the JsonSerializerSettings-taking overloads of ToJson / GetJson / ReadJson / WriteJson / UpdateJson and the JObject-returning GetJson/ReadJson/UpdateJson(Action<JObject>) are gone. Equivalent STJ surface: same method names taking JsonSerializerOptions (now defaulted to DefaultSerializerOptions when omitted), plus GetJsonObject / ReadJsonObject / UpdateJsonObject(Action<JsonObject>) for JsonNode-based access.
β’ **Migration**: replace obj.ToJson(settings) with obj.ToJson(JsonExtensions.DefaultSerializerOptions) (or pass your own JsonSerializerOptions); replace content.GetJson<JObject>() with content.GetJsonObject(); replace JObject.GetPropertyValue<T>(name) with JsonObject.GetPropertyValue<T>(name) (same method name, different receiver type via using System.Text.Json.Nodes).
β’ **Fixed Fallout.SolutionModel 10.2.24β10.2.34 unrestorable*β’ (#107): the vendored SolutionPersistence wrapper was IsPackable=false, so dotnet pack fell back to emitting the dependency under the *assemblyβ’ name (Microsoft.VisualStudio.SolutionPersistence) at the Fallout version β which doesn't exist on nuget.org. Wrapper now packs as Fallout.VisualStudio.SolutionPersistence (PackageId set explicitly; AssemblyName preserved for drop-in type identity), so Fallout.SolutionModel.nuspec declares the correct transitive dep. Also affected Fallout.Common, Fallout.Build, Fallout.ProjectModel, Fallout.Components β all fixed.
β’ **--skip-duplicate on the publish push*β’ (#108): release.yml's IPublish.PushSettings now enables skip-duplicate, so a partial publish failure (e.g. one package's API key permission gap) no longer makes reruns error on the already-uploaded versions. Pipeline is idempotent.
β’ **Fixed build.ps1 bootstrap on PowerShell 7.5+*β’ (#15): the script now uses Join-Path for SDK resolution so newer PowerShell's stricter path handling doesn't break the launcher.
β’ **Fixed solution folder names starting with digits*β’ (#16): StronglyTypedSolutionGenerator prefixes a leading underscore when the folder name would produce an invalid C# identifier.
β’ **Retired GitVersion.Tool*β’ (#81) β version is now sourced exclusively from Nerdbank.GitVersioning via version.json. MajorMinorPatchVersion and friends in Build.cs read NB.GV's $(Version) MSBuild output.
β’ **Trimmed unused / replaceable dependencies*β’ (#75, #78, #79, #80, #82): dropped JetBrains.ReSharper.GlobalTools, the matkoch Spectre.Console fork (swapped for upstream Spectre.Console), Microsoft.ApplicationInsights, Codecov.Tool, and xunit.runner.console. See docs/dependencies.md for the current graph.
β’ **Bumped Scriban 7.1.0 β 7.2.0*β’ (#84) to clear NU1903.
β’ **Fixed [GitHubActions] CheckoutRef breaking cross-repo / fork PRs**. The generator emitted ref: ${{ github.head_ref }} but didn't set repository:, so the default ${{ github.repository }} was used. For PRs from a fork, the source branch only exists on the fork; checkout tried to resolve it on origin and failed with branch or tag could not be found (regressed in #175). Generator now also emits repository: ${{ github.event.pull_request.head.repo.full_name || github.repository }} whenever CheckoutRef is set β works for fork PRs, same-repo PRs, AND push events (the || fallback). Regenerate via any dotnet fallout <target> to pick up the fix.
β’ **Fixed fallout-migrate producing unrestorable _build.csproj*β’ (closes #217). The Nuke β Fallout namespace rewrite carried the original NUKE Version=10.1.0 pins onto Fallout.* packages β but Fallout.* was never published at 10.1.0, so NuGet hit NU1603 (not found, falling back to next-higher) and WarningsAsErrors in the migrated project escalated to fatal. Migrate now bumps inline Version=... attributes on Nuke β Fallout PackageReferences to the running migrate tool's own version in the same regex pass, and strips the stale System.Security.Cryptography.Xml <PackageReference> (NUKE-era projects often pinned an older major that conflicts with Fallout.Common's transitive β₯ 10.0.6 β NU1605 downgrade). CPM-managed references (no inline Version) keep namespace-only rewrite β version stays in Directory.Packages.props.
### Added
β’ **Extracted Fallout.Core β the pure domain β’ graph foundation*β’ (#88, v11 plugin-architecture foundation). New bottom-layer project (netstandard2.1;net10.0) holding the side-effect-free types of the build execution pipeline: ITargetModel (read-only target projection β the seed for the v12 plugin SDK), ExecutionStatus, and TopoSort / PlanResult<T> (pure topological sort β’ strongly-connected-component cycle detection). Fallout.Core references nothing else in the repo and is held to a strict purity bar β no System.IO, Process, Console, or Serilog β enforced by a NetArchTest architecture-fitness test (broader suite tracked in #95).
β’ **Fully backward-compatible.*β’ ExecutionStatus moved assemblies (Fallout.Build β Fallout.Core) but is [TypeForwardedTo], and namespaces are unchanged, so existing consumer using Fallout.Common.Execution; code compiles untouched. The live ExecutableTarget stays in Fallout.Build and now implements ITargetModel; ExecutionPlanner is reduced to a thin orchestration wrapper over Fallout.Core's pure TopoSort (it still owns reading the strict parameter, failing the build on a cycle, and mutating target state).
β’ The graph primitives (Vertex / StronglyConnectedComponent*) moved from Fallout.Utilities into Fallout.Core as an internal implementation detail of TopoSort; they had no external consumers.
### Process
β’ **Added Default to backwards compatibility rule to the AGENTS.md AI brief*β’ (#262). Critical rules now state the principle explicitly β prefer additive over breaking changes; [Obsolete], transition shims, and overload-based extension before a hard break. The existing breaking-change PR-creation flow (label β’ β οΈ callout β’ version.json bump β’ CHANGELOG entry with migration path) remains the mechanics for when a break is unavoidable. Issue [#262](https://github.com/ChrisonSimtian/Fallout/issues/262) stays open for the broader policy discussion (LTS stance for the Nuke.* shims, what qualifies as backwards compatible for on-disk formats and CI workflow generators, when transition-shim generators are the right answer, etc.).
Full changelog at https://github.com/ChrisonSimtian/Fallout/blob/main/CHANGELOG.md