diff --git a/Arc.slnx b/Arc.slnx
index 1b4c92104..a2e6a6599 100644
--- a/Arc.slnx
+++ b/Arc.slnx
@@ -16,6 +16,7 @@
+
diff --git a/Source/DotNET/Arc.Core.Generators.Integration/Specs/for_CratisCodeAnalysisPackage/when_packing_cratis_codeanalysis.cs b/Source/DotNET/Arc.Core.Generators.Integration/Specs/for_CratisCodeAnalysisPackage/when_packing_cratis_codeanalysis.cs
new file mode 100644
index 000000000..d5735c653
--- /dev/null
+++ b/Source/DotNET/Arc.Core.Generators.Integration/Specs/for_CratisCodeAnalysisPackage/when_packing_cratis_codeanalysis.cs
@@ -0,0 +1,91 @@
+// Copyright (c) Cratis. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Diagnostics;
+using System.IO.Compression;
+
+namespace Cratis.Arc.Core.Generators.Integration.Specs.for_CratisCodeAnalysisPackage;
+
+///
+/// Verifies that the Cratis.CodeAnalysis umbrella package bundles every Cratis Arc lint analyzer, so a single
+/// reference brings the full ARC*/ARCCHR* set.
+///
+public class when_packing_cratis_codeanalysis
+{
+ ///
+ /// Verifies that packing Cratis.CodeAnalysis bundles the Arc and Arc.Chronicle analyzer assemblies under
+ /// analyzers/dotnet/cs.
+ ///
+ /// Thrown when packing does not produce a Cratis.CodeAnalysis package.
+ [Fact]
+ public void should_bundle_the_arc_and_chronicle_lint_analyzers()
+ {
+ var repositoryRoot = GetRepositoryRoot();
+ var workingDirectory = Path.Combine(Path.GetTempPath(), "Cratis.CodeAnalysis.Package.Integration", Guid.NewGuid().ToString("N"));
+ var packageDirectory = Path.Combine(workingDirectory, "packages");
+
+ Directory.CreateDirectory(packageDirectory);
+ RunDotNet(
+ repositoryRoot,
+ $"pack \"{Path.Combine(repositoryRoot, "Source", "DotNET", "Cratis.CodeAnalysis", "Cratis.CodeAnalysis.csproj")}\" -c Release --output \"{packageDirectory}\" -p:IncludeSymbols=false -p:IncludeSource=false");
+
+ var packagePath = Directory.GetFiles(packageDirectory, "Cratis.CodeAnalysis.*.nupkg", SearchOption.TopDirectoryOnly)
+ .OrderDescending()
+ .FirstOrDefault()
+ ?? throw new InvalidOperationException("Expected a packed Cratis.CodeAnalysis nupkg to be created.");
+
+ using var package = ZipFile.OpenRead(packagePath);
+ var analyzerEntries = package.Entries
+ .Select(_ => _.FullName)
+ .Where(_ => _.StartsWith("analyzers/dotnet/cs/", StringComparison.Ordinal))
+ .ToArray();
+
+ analyzerEntries.ShouldContain("analyzers/dotnet/cs/Cratis.Arc.Core.CodeAnalysis.dll");
+ analyzerEntries.ShouldContain("analyzers/dotnet/cs/Cratis.Arc.Chronicle.CodeAnalysis.dll");
+ }
+
+ static string GetRepositoryRoot()
+ {
+ var current = new DirectoryInfo(AppContext.BaseDirectory);
+
+ while (current is not null)
+ {
+ if (File.Exists(Path.Combine(current.FullName, "Arc.slnx")))
+ {
+ return current.FullName;
+ }
+
+ current = current.Parent;
+ }
+
+ throw new InvalidOperationException("Could not locate repository root from integration spec output directory.");
+ }
+
+ static void RunDotNet(string workingDirectory, string arguments)
+ {
+ using var process = new Process
+ {
+ StartInfo = new()
+ {
+ FileName = "dotnet",
+ Arguments = arguments,
+ WorkingDirectory = workingDirectory,
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ UseShellExecute = false,
+ }
+ };
+
+ process.Start();
+ var standardOutputTask = process.StandardOutput.ReadToEndAsync();
+ var standardErrorTask = process.StandardError.ReadToEndAsync();
+ process.WaitForExit();
+ var standardOutput = standardOutputTask.GetAwaiter().GetResult();
+ var standardError = standardErrorTask.GetAwaiter().GetResult();
+
+ if (process.ExitCode != 0)
+ {
+ throw new InvalidOperationException($"dotnet {arguments} failed with exit code {process.ExitCode}.{Environment.NewLine}{standardOutput}{Environment.NewLine}{standardError}");
+ }
+ }
+}
diff --git a/Source/DotNET/Arc.Core.Generators/Arc.Core.Generators.csproj b/Source/DotNET/Arc.Core.Generators/Arc.Core.Generators.csproj
index 4e3a8f51f..7ba7460a2 100644
--- a/Source/DotNET/Arc.Core.Generators/Arc.Core.Generators.csproj
+++ b/Source/DotNET/Arc.Core.Generators/Arc.Core.Generators.csproj
@@ -12,13 +12,13 @@
latest
$(NoWarn);NU5128;NU1507
+
+ false
-
-
-
-
\ No newline at end of file
diff --git a/Source/DotNET/Cratis.CodeAnalysis/Cratis.CodeAnalysis.csproj b/Source/DotNET/Cratis.CodeAnalysis/Cratis.CodeAnalysis.csproj
new file mode 100644
index 000000000..6457312a3
--- /dev/null
+++ b/Source/DotNET/Cratis.CodeAnalysis/Cratis.CodeAnalysis.csproj
@@ -0,0 +1,38 @@
+
+
+ Cratis.CodeAnalysis
+ Cratis.CodeAnalysis
+ The full set of Cratis Arc Roslyn analyzers (ARC*, ARCCHR*) in a single reference. Opt in to all the analyzers at once instead of referencing each analyzer package individually.
+ cratis;arc;chronicle;analyzers;roslyn;codeanalysis
+
+ false
+ false
+ false
+ true
+ true
+ netstandard2.0
+
+ $(NoWarn);NU5128
+
+
+
+
+
+
+
+
+
+
+
+
+
+