From 0a45a2c7db77eafc3d301dbe0499d280ac491d82 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 6 Apr 2026 05:22:48 +0000
Subject: [PATCH 1/5] Initial plan
From c6c6899d96b17feae4424e9a87be72d2681af260 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 6 Apr 2026 05:32:00 +0000
Subject: [PATCH 2/5] feat(scribe): add solution-local analyzer support
(auto-pack & inject)
Agent-Logs-Url: https://github.com/BulletsForHumanity/Scribe/sessions/ea27be1c-a52e-4dac-aa4f-6fac868887c5
Co-authored-by: crazycrank <11194014+crazycrank@users.noreply.github.com>
---
README.md | 2 +
Scribe.Sdk/README.md | 18 ++
Scribe.Sdk/Scribe.Sdk.csproj | 6 +
Scribe.Sdk/Sdk/Sdk.props | 3 +
Scribe.Sdk/Sdk/Sdk.targets | 3 +
Scribe/build/Scribe.SolutionAnalyzer.props | 83 ++++++
Scribe/build/Scribe.SolutionAnalyzer.targets | 46 ++++
docs/architecture-infrastructure.md | 60 ++++-
docs/project-setup.md | 28 ++
docs/solution-local-analyzers.md | 254 +++++++++++++++++++
10 files changed, 500 insertions(+), 3 deletions(-)
create mode 100644 Scribe/build/Scribe.SolutionAnalyzer.props
create mode 100644 Scribe/build/Scribe.SolutionAnalyzer.targets
create mode 100644 docs/solution-local-analyzers.md
diff --git a/README.md b/README.md
index ec0fee8..6574791 100644
--- a/README.md
+++ b/README.md
@@ -177,6 +177,7 @@ See [Project Setup & Infrastructure](docs/project-setup.md#local-development-loc
| Component | Purpose |
|-----------|---------|
| [**Scribe SDK**](docs/project-setup.md#scribe-sdk-recommended) | MSBuild SDK — zero-config project setup for analyzer/generator projects |
+| [**Solution-Local Analyzers**](docs/solution-local-analyzers.md) | Auto-pack intra-solution analyzers — no NuGet publishing needed |
| [**Quill**](docs/quill-reference.md) | Fluent source builder — indentation, usings, namespaces, XML docs, type resolution |
| **ScribeHeader** | Assembly-level attribute for branding generated files with a decorative page header |
| **Template** | Minimal `{{key}}` marker substitution for structural shells |
@@ -194,6 +195,7 @@ See [Project Setup & Infrastructure](docs/project-setup.md#local-development-loc
|-------|---------------|
| [Writing Generators with Quill](docs/writing-generators.md) | How to structure a generator using the Transform → Register → Render pattern |
| [Project Setup & Infrastructure](docs/project-setup.md) | .csproj configuration, packaging, LocalDev multi-repo workflow |
+| [Solution-Local Analyzers](docs/solution-local-analyzers.md) | Setup guide for intra-solution analyzers and generators |
| [Quill Feature Reference](docs/quill-reference.md) | Complete API reference for every Quill method and scope type |
| [How Quill Works](docs/architecture-quill.md) | Internal architecture — type resolution, indentation, XML doc insertion |
| [How the Infrastructure Works](docs/architecture-infrastructure.md) | Build system internals — LocalDev props/targets, override file generation |
diff --git a/Scribe.Sdk/README.md b/Scribe.Sdk/README.md
index ddc482a..9b8e199 100644
--- a/Scribe.Sdk/README.md
+++ b/Scribe.Sdk/README.md
@@ -80,6 +80,24 @@ All polyfills are guarded by `#if !NET5_0_OR_GREATER` and deactivate on modern t
The SDK includes [LocalDev](https://github.com/BulletsForHumanity/Scribe/blob/master/docs/project-setup.md#local-development-localdev) infrastructure for multi-repo development. See the main Scribe documentation for setup instructions.
+## Solution-Local Analyzers
+
+For analyzers that live inside the same solution they serve, set `ScribeSolutionAnalyzer=true` to auto-pack on build:
+
+```xml
+
+
+ true
+
+
+
+
+
+
+```
+
+See [Solution-Local Analyzers](https://github.com/BulletsForHumanity/Scribe/blob/master/docs/solution-local-analyzers.md) for the complete setup guide.
+
## Links
- [Scribe Documentation](https://github.com/BulletsForHumanity/Scribe)
diff --git a/Scribe.Sdk/Scribe.Sdk.csproj b/Scribe.Sdk/Scribe.Sdk.csproj
index 89a7dd6..2146d66 100644
--- a/Scribe.Sdk/Scribe.Sdk.csproj
+++ b/Scribe.Sdk/Scribe.Sdk.csproj
@@ -38,6 +38,12 @@
+
+
+
+
+
+
diff --git a/Scribe.Sdk/Sdk/Sdk.props b/Scribe.Sdk/Sdk/Sdk.props
index 6c231cb..8471c46 100644
--- a/Scribe.Sdk/Sdk/Sdk.props
+++ b/Scribe.Sdk/Sdk/Sdk.props
@@ -56,6 +56,9 @@
true
+
+
+
diff --git a/Scribe.Sdk/Sdk/Sdk.targets b/Scribe.Sdk/Sdk/Sdk.targets
index 9e0f681..3832a11 100644
--- a/Scribe.Sdk/Sdk/Sdk.targets
+++ b/Scribe.Sdk/Sdk/Sdk.targets
@@ -53,6 +53,9 @@
+
+
+
diff --git a/Scribe/build/Scribe.SolutionAnalyzer.props b/Scribe/build/Scribe.SolutionAnalyzer.props
new file mode 100644
index 0000000..ca974c5
--- /dev/null
+++ b/Scribe/build/Scribe.SolutionAnalyzer.props
@@ -0,0 +1,83 @@
+
+
+
+
+
+
+ 0.0.0-local
+ 0.0.0-local
+
+
+ true
+ true
+
+
+ false
+
+
+
+
+
+ $(SolutionDir).packages\
+ $(MSBuildProjectDirectory)\..\.packages\
+
+
+
+ $(ScribeSolutionPackagesDir)
+
+
+
+
+
+
+ $(RestoreAdditionalProjectSources);$(ScribeSolutionPackagesDir)
+
+
+
diff --git a/Scribe/build/Scribe.SolutionAnalyzer.targets b/Scribe/build/Scribe.SolutionAnalyzer.targets
new file mode 100644
index 0000000..decfb80
--- /dev/null
+++ b/Scribe/build/Scribe.SolutionAnalyzer.targets
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+ <_ScribeSaCacheDir>$(NuGetPackageRoot)$([System.String]::Copy('$(PackageId)').ToLowerInvariant())\$(PackageVersion)\
+ <_ScribeSaNupkgPath>$(ScribeSolutionPackagesDir)$(PackageId).$(PackageVersion).nupkg
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/architecture-infrastructure.md b/docs/architecture-infrastructure.md
index 29f75ab..063d3fd 100644
--- a/docs/architecture-infrastructure.md
+++ b/docs/architecture-infrastructure.md
@@ -1,6 +1,6 @@
# How the Infrastructure Works
-Internal architecture of Scribe's build infrastructure — the Scribe SDK and LocalDev system. Read this if you're contributing to Scribe or want to understand the MSBuild mechanics.
+Internal architecture of Scribe's build infrastructure — the Scribe SDK, Solution-Local Analyzer, and LocalDev systems. Read this if you're contributing to Scribe or want to understand the MSBuild mechanics.
For setup instructions, see [Project Setup & Infrastructure](project-setup.md).
@@ -20,6 +20,8 @@ BulletsForHumanity.Scribe.Sdk.nupkg
build/
Scribe.LocalDev.props <- Shared LocalDev infrastructure (early phase)
Scribe.LocalDev.targets <- Shared LocalDev infrastructure (late phase)
+ Scribe.SolutionAnalyzer.props <- Solution-local analyzer support (early phase)
+ Scribe.SolutionAnalyzer.targets <- Solution-local analyzer support (late phase)
content/
Stubs.cs <- netstandard2.0 polyfills, injected as Compile item
```
@@ -43,7 +45,8 @@ Runs before the project file is evaluated. Sets overridable defaults:
1. **Chains to `Microsoft.NET.Sdk`** via ``
2. **Sets analyzer project defaults**: `TargetFramework=netstandard2.0`, `LangVersion=14`, `EnforceExtendedAnalyzerRules=true`, `IncludeBuildOutput=false`, `PackageType=Analyzer`, embedded PDB, nullable enabled
3. **Initialises `ScribeSdkIncludeStubs`** to `true` (opt-out via `false`)
-4. **Imports `Scribe.LocalDev.props`** for sentinel file detection and local NuGet source registration
+4. **Imports `Scribe.SolutionAnalyzer.props`** for solution-local analyzer auto-pack configuration
+5. **Imports `Scribe.LocalDev.props`** for sentinel file detection and local NuGet source registration
All properties can be overridden by the consuming `.csproj` because they're set in the early phase.
@@ -56,7 +59,58 @@ Runs after the project file. Enforces packaging behaviour:
3. **`_ScribeSdkAddAnalyzerDlls` target**: Places the analyzer DLL into `analyzers/dotnet/cs/` in the NuGet package
4. **`_ScribeSdkAddAnalyzerDependencies` target**: Bundles private NuGet dependencies alongside the analyzer DLL. Excludes `Microsoft.CodeAnalysis.*` DLLs (provided by the compiler host).
5. **Sets `CopyLocalLockFileAssemblies=true`** to enable dependency bundling
-6. **Imports `Scribe.LocalDev.targets`** for version override wildcard import
+6. **Imports `Scribe.SolutionAnalyzer.targets`** for solution-local cache invalidation
+7. **Imports `Scribe.LocalDev.targets`** for version override wildcard import
+
+---
+
+## Solution-Local Analyzer
+
+The Solution-Local Analyzer infrastructure enables intra-solution analyzer development — analyzers that live inside the same solution as the projects they serve, without publishing to NuGet.
+
+For setup instructions, see [Solution-Local Analyzers](solution-local-analyzers.md).
+
+### File Layout
+
+```
+Scribe/build/
+ Scribe.SolutionAnalyzer.props <- Configuration (early phase)
+ Scribe.SolutionAnalyzer.targets <- Cache invalidation target (late phase)
+```
+
+### Props Phase (Early)
+
+`Scribe.SolutionAnalyzer.props` runs when `ScribeSolutionAnalyzer=true`:
+
+1. **Locks the version** to `0.0.0-local` — disables NBGV (`NerdbankGitVersioningEnabled=false`)
+2. **Enables auto-pack** via `GeneratePackageOnBuild=true`
+3. **Resolves the package directory** — defaults to `.packages/` at the solution root (via `$(SolutionDir)` or parent of project directory)
+4. **Redirects pack output** to `$(ScribeSolutionPackagesDir)`
+5. **Registers as NuGet source** via `RestoreAdditionalProjectSources` (for the analyzer project itself)
+
+### Targets Phase (Late)
+
+`Scribe.SolutionAnalyzer.targets` defines one target:
+
+**`_ScribeSolutionAnalyzerClearCache`** — Runs before `GenerateNuspec` (which precedes Pack). Removes:
+- The cached package extraction from the NuGet global packages folder (`~/.nuget/packages//0.0.0-local/`)
+- The old `.nupkg` from the `.packages/` directory
+
+This ensures consuming projects always pick up the freshly-built package without requiring unique version numbers.
+
+### Relationship to LocalDev
+
+Solution-Local Analyzer and LocalDev are complementary:
+
+| Concern | LocalDev | Solution-Local Analyzer |
+|---------|----------|------------------------|
+| Scope | Cross-repo | Intra-solution |
+| Trigger | `.localscribe` sentinel | `ScribeSolutionAnalyzer=true` |
+| Version | NBGV + timestamp suffix | Fixed `0.0.0-local` |
+| Override files | Generated `.Directory.Packages.targets` | None needed |
+| Package directory | Shared `/.artifacts/packages/` | Solution-local `.packages/` |
+
+Both features are independent and can coexist. A solution can have solution-local analyzers and also participate in a LocalDev chain.
---
diff --git a/docs/project-setup.md b/docs/project-setup.md
index 1287950..fc57337 100644
--- a/docs/project-setup.md
+++ b/docs/project-setup.md
@@ -143,6 +143,34 @@ For test projects that instantiate generators directly via `CSharpGeneratorDrive
---
+## Solution-Local Analyzers
+
+If your analyzer or generator lives inside the same solution it serves (and is not published to NuGet), the Scribe SDK can auto-pack it on build and make it available to other projects in the solution via standard `PackageReference`.
+
+Set `ScribeSolutionAnalyzer=true` in your analyzer `.csproj`:
+
+```xml
+
+
+ true
+
+
+
+
+
+
+```
+
+Consuming projects reference it like any NuGet package:
+
+```xml
+
+```
+
+See [Solution-Local Analyzers](solution-local-analyzers.md) for the complete setup guide, including NuGet source registration and troubleshooting.
+
+---
+
## Packaging
### Placing analyzer DLLs
diff --git a/docs/solution-local-analyzers.md b/docs/solution-local-analyzers.md
new file mode 100644
index 0000000..02f5527
--- /dev/null
+++ b/docs/solution-local-analyzers.md
@@ -0,0 +1,254 @@
+# Solution-Local Analyzers
+
+How to set up Roslyn analyzers and source generators that live inside the same solution they serve — no NuGet publishing, no cross-repo setup.
+
+---
+
+## Overview
+
+A **solution-local analyzer** is a Roslyn analyzer or source generator project that exists inside the same solution as the projects it serves. It is not published to NuGet. It is not consumed cross-repo. It automates *this* codebase and nothing else.
+
+Examples:
+
+- A generator that reads domain model types and emits mapping code, DTOs, or validation rules
+- An analyzer that enforces solution-specific coding conventions
+- A generator that emits strongly-typed configuration accessors from appsettings schema
+
+The Scribe SDK provides first-class support for this workflow. When `ScribeSolutionAnalyzer=true` is set on a Scribe SDK project, the SDK automatically:
+
+1. **Packs the analyzer on every build** — produces a `.nupkg` in a solution-local `.packages/` directory
+2. **Uses a fixed local version** (`0.0.0-local`) — no version management needed
+3. **Clears the NuGet cache** — consuming projects always get the freshly-built package
+4. **Bundles private dependencies** — the same dependency bundling as any Scribe SDK project
+
+Consuming projects reference the analyzer via standard ``, getting all the same analyzer wiring that any published package gets.
+
+---
+
+## Quick Start
+
+### 1. Create the analyzer project
+
+```xml
+
+
+ true
+
+
+
+
+
+
+```
+
+### 2. Register the local package source
+
+Add a `NuGet.config` at the solution root (one-time setup):
+
+```xml
+
+
+
+
+
+
+
+```
+
+Alternatively, add to your `Directory.Build.props`:
+
+```xml
+
+
+ $(RestoreAdditionalProjectSources);$(MSBuildThisFileDirectory).packages
+
+
+```
+
+### 3. Reference from consuming projects
+
+```xml
+
+
+
+```
+
+Or with [Central Package Management](https://learn.microsoft.com/en-us/nuget/consume-packages/central-package-management):
+
+```xml
+
+
+```
+
+### 4. Add `.packages/` to `.gitignore`
+
+```gitignore
+.packages/
+```
+
+### 5. Build
+
+```shell
+dotnet build MySolution.sln
+```
+
+The analyzer auto-packs on build. Consuming projects resolve the package from the local `.packages/` directory. Diagnostics and generated code appear automatically.
+
+---
+
+## How It Works
+
+### Auto-Pack on Build
+
+When `ScribeSolutionAnalyzer=true`, the SDK sets `GeneratePackageOnBuild=true` and redirects `PackageOutputPath` to the `.packages/` directory. Every build produces a fresh `.nupkg`.
+
+### Fixed Version
+
+The version is locked to `0.0.0-local`. This avoids version management complexity — there's no NBGV, no timestamps, no override files. Consuming projects always reference `Version="0.0.0-local"`.
+
+### Cache Invalidation
+
+NuGet caches extracted packages in the global packages folder (`~/.nuget/packages/`). With a fixed version, the cache would serve stale content. The SDK handles this automatically: before each pack, the `_ScribeSolutionAnalyzerClearCache` target removes the cached extraction and the old `.nupkg`, forcing NuGet to re-extract the freshly-built package on the next restore.
+
+### Package Directory
+
+By default, packages are placed in `.packages/` relative to the solution directory. The SDK resolves the location in this order:
+
+1. `$(ScribeSolutionPackagesDir)` — if explicitly set by the developer
+2. `$(SolutionDir).packages\` — when building via a solution file or from Visual Studio
+3. `$(MSBuildProjectDirectory)\..\.packages\` — fallback for individual project builds
+
+Override the default in your `Directory.Build.props`:
+
+```xml
+
+ $(MSBuildThisFileDirectory).packages\
+
+```
+
+---
+
+## Solution Layout
+
+A typical solution with a solution-local analyzer:
+
+```
+MySolution/
+ MySolution.sln
+ NuGet.config ← registers .packages/ as a source
+ Directory.Build.props ← (optional) shared properties
+ .gitignore ← includes .packages/
+ .packages/ ← auto-generated, git-ignored
+ MyAnalyzer.0.0.0-local.nupkg
+ MyAnalyzer/
+ MyAnalyzer.csproj ← ScribeSolutionAnalyzer=true
+ MyGenerator.cs
+ MyApp/
+ MyApp.csproj ←
+ Program.cs
+```
+
+---
+
+## Multiple Analyzers
+
+A solution can have multiple solution-local analyzers. All pack to the same `.packages/` directory:
+
+```xml
+
+
+
+ true
+
+ ...
+
+```
+
+```xml
+
+
+
+ true
+
+ ...
+
+```
+
+```xml
+
+
+
+
+
+```
+
+---
+
+## Relationship to LocalDev
+
+Solution-local analyzers and LocalDev solve different problems:
+
+| Concern | LocalDev | Solution-Local Analyzer |
+|---------|----------|------------------------|
+| Scope | Cross-repo (e.g. Scribe → Hermetic → MyApp) | Intra-solution |
+| Trigger | `.localscribe` sentinel file | `ScribeSolutionAnalyzer=true` property |
+| Published? | Yes (eventually to NuGet) | Never |
+| Version management | NBGV + timestamp + override files | Fixed `0.0.0-local` |
+| Package directory | Shared `/.artifacts/packages/` | Solution-local `.packages/` |
+| Setup complexity | `$(ScribeRoot)`, trigger project, package names | One property + NuGet source |
+
+If your analyzer is (or will become) a standalone package consumed across repositories, use the standard Scribe SDK workflow with [LocalDev](project-setup.md#local-development-localdev). If it exists solely to serve the solution it lives in, use `ScribeSolutionAnalyzer`.
+
+---
+
+## Provided Properties
+
+| Property | Value | Set by |
+|----------|-------|--------|
+| `$(ScribeSolutionAnalyzer)` | `true` | Developer (in `.csproj`) |
+| `$(ScribeSolutionPackagesDir)` | `.packages/` at solution root | SDK (overridable) |
+| `$(Version)` | `0.0.0-local` | SDK |
+| `$(PackageVersion)` | `0.0.0-local` | SDK |
+| `$(GeneratePackageOnBuild)` | `true` | SDK |
+| `$(PackageOutputPath)` | `$(ScribeSolutionPackagesDir)` | SDK |
+
+---
+
+## Design Decisions
+
+| Decision | Choice | Rationale |
+|----------|--------|-----------|
+| Detection mechanism | Explicit `ScribeSolutionAnalyzer=true` property | Simple, discoverable, no magic. Convention-based detection would be ambiguous. |
+| Version strategy | Fixed `0.0.0-local` | Avoids version management complexity. Cache invalidation is handled by a build target. |
+| Package directory | `.packages/` at solution root | Mirrors `.artifacts/` convention. Solution-scoped, git-ignored. |
+| NuGet source registration | Manual one-time setup (NuGet.config or Directory.Build.props) | The SDK can only configure projects that use it. Consuming projects need a standard NuGet mechanism. |
+| Cache invalidation | Delete from NuGet global cache before each pack | Ensures consuming projects always get the fresh package without requiring unique versions. |
+
+---
+
+## Troubleshooting
+
+### Consuming project doesn't see the analyzer
+
+1. Verify the `.packages/` directory contains a `.nupkg` for the analyzer
+2. Verify `NuGet.config` (or `RestoreAdditionalProjectSources`) includes the `.packages/` directory
+3. Run `dotnet restore` on the consuming project
+4. In Visual Studio, try **Build → Rebuild Solution**
+
+### Stale generated code after analyzer changes
+
+The `_ScribeSolutionAnalyzerClearCache` target should handle this automatically. If you still see stale output:
+
+1. Delete the `.packages/` directory
+2. Delete `~/.nuget/packages//0.0.0-local/`
+3. Run `dotnet restore && dotnet build`
+
+### Package directory location is wrong
+
+Set `$(ScribeSolutionPackagesDir)` explicitly in your `Directory.Build.props`:
+
+```xml
+
+ $(MSBuildThisFileDirectory).packages\
+
+```
From 26b612b85908861bbbdc21a4fbfe2cfc13267a86 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 6 Apr 2026 05:48:15 +0000
Subject: [PATCH 3/5] refactor(scribe): use timestamp version and override
files for solution-local analyzers
Agent-Logs-Url: https://github.com/BulletsForHumanity/Scribe/sessions/a0a87e65-138d-464f-bb3a-d0393d5bfd40
Co-authored-by: crazycrank <11194014+crazycrank@users.noreply.github.com>
---
Scribe.Sdk/README.md | 4 +-
Scribe/build/Scribe.SolutionAnalyzer.props | 85 +++++-----
Scribe/build/Scribe.SolutionAnalyzer.targets | 62 ++++---
docs/architecture-infrastructure.md | 38 +++--
docs/project-setup.md | 6 +-
docs/solution-local-analyzers.md | 168 ++++++++++---------
6 files changed, 197 insertions(+), 166 deletions(-)
diff --git a/Scribe.Sdk/README.md b/Scribe.Sdk/README.md
index 9b8e199..507a9a7 100644
--- a/Scribe.Sdk/README.md
+++ b/Scribe.Sdk/README.md
@@ -82,7 +82,7 @@ The SDK includes [LocalDev](https://github.com/BulletsForHumanity/Scribe/blob/ma
## Solution-Local Analyzers
-For analyzers that live inside the same solution they serve, set `ScribeSolutionAnalyzer=true` to auto-pack on build:
+For analyzers that live inside the same solution they serve, set `ScribeSolutionAnalyzer=true` to auto-pack on build with automatic version management:
```xml
@@ -96,7 +96,7 @@ For analyzers that live inside the same solution they serve, set `ScribeSolution
```
-See [Solution-Local Analyzers](https://github.com/BulletsForHumanity/Scribe/blob/master/docs/solution-local-analyzers.md) for the complete setup guide.
+Each build produces a unique timestamp version and a version override file, using the same mechanism as [LocalDev](https://github.com/BulletsForHumanity/Scribe/blob/master/docs/project-setup.md#local-development-localdev) but scoped to the solution. See [Solution-Local Analyzers](https://github.com/BulletsForHumanity/Scribe/blob/master/docs/solution-local-analyzers.md) for the complete setup guide.
## Links
diff --git a/Scribe/build/Scribe.SolutionAnalyzer.props b/Scribe/build/Scribe.SolutionAnalyzer.props
index ca974c5..b941d47 100644
--- a/Scribe/build/Scribe.SolutionAnalyzer.props
+++ b/Scribe/build/Scribe.SolutionAnalyzer.props
@@ -3,9 +3,13 @@
Scribe Solution-Local Analyzer — Property Definitions (Early Phase)
Automatically activates when a Scribe SDK project sets
- ScribeSolutionAnalyzer=true. Configures auto-pack on build, outputs to a
- solution-local .packages/ directory, and registers that directory as a
- NuGet source.
+ ScribeSolutionAnalyzer=true. Configures auto-pack on build with a
+ timestamp-based version, outputs to the solution's artifacts/packages/
+ directory, and registers that directory as a NuGet source.
+
+ Uses the same mechanism as LocalDev — the companion targets file
+ generates a version override .Directory.Packages.targets file so
+ consuming projects resolve the locally-built package automatically.
── Usage ────────────────────────────────────────────────────────────────
@@ -15,69 +19,72 @@
true
- Consuming projects reference the analyzer via standard PackageReference:
+ Consuming projects reference the analyzer via standard PackageReference.
+ The version is managed automatically via an override file — consumers
+ only need to declare the PackageVersion Include in Directory.Packages.props
+ (any version; the override file will pin the actual version).
-
+ ── Prerequisites ──────────────────────────────────────────────────────
- A NuGet.config or Directory.Build.props must register the .packages/
- directory as a package source. See docs/solution-local-analyzers.md.
+ The solution must use artifact output (UseArtifactsOutput=true) with
+ $(ArtifactsPath) set in Directory.Build.props.
- ── Provides ─────────────────────────────────────────────────────────────
+ The solution's Directory.Build.props must register the packages source:
+
+
+ $(RestoreAdditionalProjectSources);$(ArtifactsPath)packages
+
+
+ The solution's Directory.Build.targets must import override files:
+
+
- $(ScribeSolutionAnalyzer) 'true' when active.
- $(ScribeSolutionPackagesDir) Path to the solution-local .packages/ directory.
+ See docs/solution-local-analyzers.md for the complete setup guide.
- ── Customisation ────────────────────────────────────────────────────────
+ ── Provides ─────────────────────────────────────────────────────────────
- $(ScribeSolutionPackagesDir) Override the default .packages/ location.
+ $(ScribeSolutionAnalyzer) 'true' when active.
+ $(Version) Timestamp-based: 0.0.0-dev.yyyyMMddHHmmss
+ $(PackageVersion) Same as $(Version).
-->
-
- 0.0.0-local
- 0.0.0-local
+
+ <_ScribeSaTimestamp>$([System.DateTime]::UtcNow.ToString('yyyyMMddHHmmss'))
+ 0.0.0-dev.$(_ScribeSaTimestamp)
+ 0.0.0-dev.$(_ScribeSaTimestamp)
true
true
-
+
false
-
- $(SolutionDir).packages\
- $(MSBuildProjectDirectory)\..\.packages\
-
-
-
- $(ScribeSolutionPackagesDir)
+
+ $(ArtifactsPath)packages\
-
+
- $(RestoreAdditionalProjectSources);$(ScribeSolutionPackagesDir)
+ $(RestoreAdditionalProjectSources);$(ArtifactsPath)packages
diff --git a/Scribe/build/Scribe.SolutionAnalyzer.targets b/Scribe/build/Scribe.SolutionAnalyzer.targets
index decfb80..211ee1d 100644
--- a/Scribe/build/Scribe.SolutionAnalyzer.targets
+++ b/Scribe/build/Scribe.SolutionAnalyzer.targets
@@ -2,45 +2,55 @@
-
+
- <_ScribeSaCacheDir>$(NuGetPackageRoot)$([System.String]::Copy('$(PackageId)').ToLowerInvariant())\$(PackageVersion)\
- <_ScribeSaNupkgPath>$(ScribeSolutionPackagesDir)$(PackageId).$(PackageVersion).nupkg
+ <_ScribeSaVersion>$(PackageVersion)
+ <_ScribeSaOverridePath>$(ArtifactsPath)$(PackageId).Directory.Packages.targets
-
-
+
+ <_ScribeSaLine Include="<Project>" />
+ <_ScribeSaLine Include=" <!-- Auto-generated by Scribe SolutionAnalyzer — do not edit. Rebuild the analyzer to refresh. -->" />
+ <_ScribeSaLine Include=" <ItemGroup>" />
+ <_ScribeSaLine Include=" <PackageVersion Update="$(PackageId)" Version="$(_ScribeSaVersion)" />" />
+ <_ScribeSaLine Include=" </ItemGroup>" />
+ <_ScribeSaLine Include="</Project>" />
+
-
-
+
-
diff --git a/docs/architecture-infrastructure.md b/docs/architecture-infrastructure.md
index 063d3fd..d24e336 100644
--- a/docs/architecture-infrastructure.md
+++ b/docs/architecture-infrastructure.md
@@ -59,14 +59,14 @@ Runs after the project file. Enforces packaging behaviour:
3. **`_ScribeSdkAddAnalyzerDlls` target**: Places the analyzer DLL into `analyzers/dotnet/cs/` in the NuGet package
4. **`_ScribeSdkAddAnalyzerDependencies` target**: Bundles private NuGet dependencies alongside the analyzer DLL. Excludes `Microsoft.CodeAnalysis.*` DLLs (provided by the compiler host).
5. **Sets `CopyLocalLockFileAssemblies=true`** to enable dependency bundling
-6. **Imports `Scribe.SolutionAnalyzer.targets`** for solution-local cache invalidation
+6. **Imports `Scribe.SolutionAnalyzer.targets`** for solution-local override file generation
7. **Imports `Scribe.LocalDev.targets`** for version override wildcard import
---
## Solution-Local Analyzer
-The Solution-Local Analyzer infrastructure enables intra-solution analyzer development — analyzers that live inside the same solution as the projects they serve, without publishing to NuGet.
+The Solution-Local Analyzer infrastructure enables intra-solution analyzer development — analyzers that live inside the same solution as the projects they serve, without publishing to NuGet. It uses the **same mechanism** as LocalDev (version override files + NuGet source registration) but scoped to a single solution.
For setup instructions, see [Solution-Local Analyzers](solution-local-analyzers.md).
@@ -75,40 +75,48 @@ For setup instructions, see [Solution-Local Analyzers](solution-local-analyzers.
```
Scribe/build/
Scribe.SolutionAnalyzer.props <- Configuration (early phase)
- Scribe.SolutionAnalyzer.targets <- Cache invalidation target (late phase)
+ Scribe.SolutionAnalyzer.targets <- Override file generation (late phase)
```
### Props Phase (Early)
`Scribe.SolutionAnalyzer.props` runs when `ScribeSolutionAnalyzer=true`:
-1. **Locks the version** to `0.0.0-local` — disables NBGV (`NerdbankGitVersioningEnabled=false`)
-2. **Enables auto-pack** via `GeneratePackageOnBuild=true`
-3. **Resolves the package directory** — defaults to `.packages/` at the solution root (via `$(SolutionDir)` or parent of project directory)
-4. **Redirects pack output** to `$(ScribeSolutionPackagesDir)`
+1. **Generates a timestamp version** `0.0.0-dev.yyyyMMddHHmmss` — forces Visual Studio to reload analyzers on every build
+2. **Disables NBGV** (`NerdbankGitVersioningEnabled=false`) — version is managed by the SDK
+3. **Enables auto-pack** via `GeneratePackageOnBuild=true`
+4. **Redirects pack output** to `$(ArtifactsPath)packages/`
5. **Registers as NuGet source** via `RestoreAdditionalProjectSources` (for the analyzer project itself)
### Targets Phase (Late)
`Scribe.SolutionAnalyzer.targets` defines one target:
-**`_ScribeSolutionAnalyzerClearCache`** — Runs before `GenerateNuspec` (which precedes Pack). Removes:
-- The cached package extraction from the NuGet global packages folder (`~/.nuget/packages//0.0.0-local/`)
-- The old `.nupkg` from the `.packages/` directory
+**`_ScribeSolutionAnalyzerOverride`** — Runs after Pack. Generates a `.Directory.Packages.targets` file in `$(ArtifactsPath)` containing a `PackageVersion Update` entry:
-This ensures consuming projects always pick up the freshly-built package without requiring unique version numbers.
+```xml
+
+
+
+
+
+
+```
+
+Consuming projects import this file via wildcard from `$(ArtifactsPath)` in their `Directory.Build.targets`. This is the same pattern as LocalDev's `_ScribeLocalDevOverride` target.
### Relationship to LocalDev
-Solution-Local Analyzer and LocalDev are complementary:
+Solution-Local Analyzer and LocalDev use the same underlying mechanism but at different scopes:
| Concern | LocalDev | Solution-Local Analyzer |
|---------|----------|------------------------|
| Scope | Cross-repo | Intra-solution |
| Trigger | `.localscribe` sentinel | `ScribeSolutionAnalyzer=true` |
-| Version | NBGV + timestamp suffix | Fixed `0.0.0-local` |
-| Override files | Generated `.Directory.Packages.targets` | None needed |
-| Package directory | Shared `/.artifacts/packages/` | Solution-local `.packages/` |
+| Version | NBGV + timestamp suffix | Timestamp-only (`0.0.0-dev.yyyyMMddHHmmss`) |
+| Override files | Generated `.Directory.Packages.targets` | Same — generated `.Directory.Packages.targets` |
+| Package directory | Shared `$(ScribeRoot)/.artifacts/packages/` | Solution `$(ArtifactsPath)packages/` |
+| Override file location | `$(ScribeRoot)/.artifacts/` | `$(ArtifactsPath)` |
Both features are independent and can coexist. A solution can have solution-local analyzers and also participate in a LocalDev chain.
diff --git a/docs/project-setup.md b/docs/project-setup.md
index fc57337..0b23244 100644
--- a/docs/project-setup.md
+++ b/docs/project-setup.md
@@ -161,13 +161,13 @@ Set `ScribeSolutionAnalyzer=true` in your analyzer `.csproj`:
```
-Consuming projects reference it like any NuGet package:
+Consuming projects reference it like any NuGet package (the version is managed automatically via an override file):
```xml
-
+
```
-See [Solution-Local Analyzers](solution-local-analyzers.md) for the complete setup guide, including NuGet source registration and troubleshooting.
+See [Solution-Local Analyzers](solution-local-analyzers.md) for the complete setup guide, including consumer-side `Directory.Build.props` and `.targets` configuration.
---
diff --git a/docs/solution-local-analyzers.md b/docs/solution-local-analyzers.md
index 02f5527..84986da 100644
--- a/docs/solution-local-analyzers.md
+++ b/docs/solution-local-analyzers.md
@@ -16,9 +16,9 @@ Examples:
The Scribe SDK provides first-class support for this workflow. When `ScribeSolutionAnalyzer=true` is set on a Scribe SDK project, the SDK automatically:
-1. **Packs the analyzer on every build** — produces a `.nupkg` in a solution-local `.packages/` directory
-2. **Uses a fixed local version** (`0.0.0-local`) — no version management needed
-3. **Clears the NuGet cache** — consuming projects always get the freshly-built package
+1. **Packs the analyzer on every build** — produces a `.nupkg` in the solution's `artifacts/packages/` directory
+2. **Generates a unique timestamp version** (`0.0.0-dev.yyyyMMddHHmmss`) — forces Visual Studio to reload the analyzer on every build
+3. **Generates a version override file** — consuming projects automatically resolve the locally-built package via the same mechanism as [LocalDev](project-setup.md#local-development-localdev)
4. **Bundles private dependencies** — the same dependency bundling as any Scribe SDK project
Consuming projects reference the analyzer via standard ``, getting all the same analyzer wiring that any published package gets.
@@ -41,58 +41,55 @@ Consuming projects reference the analyzer via standard ``, get
```
-### 2. Register the local package source
+### 2. Configure the solution for local packages
-Add a `NuGet.config` at the solution root (one-time setup):
+The solution must use artifact output. In `Directory.Build.props`:
```xml
-
-
-
-
-
-
-
-```
-
-Alternatively, add to your `Directory.Build.props`:
+
+ true
+ $(MSBuildThisFileDirectory).artifacts/
+
-```xml
+
- $(RestoreAdditionalProjectSources);$(MSBuildThisFileDirectory).packages
+ $(RestoreAdditionalProjectSources);$(ArtifactsPath)packages
```
-### 3. Reference from consuming projects
+In `Directory.Build.targets`, import override files generated by solution-local analyzers:
```xml
-
-
-
+
```
-Or with [Central Package Management](https://learn.microsoft.com/en-us/nuget/consume-packages/central-package-management):
+### 3. Reference from consuming projects
+
+Add the analyzer to your `Directory.Packages.props` (any version — the override file pins the actual version):
```xml
-
+
```
-### 4. Add `.packages/` to `.gitignore`
+Then reference it from consuming projects:
-```gitignore
-.packages/
+```xml
+
+
+
```
-### 5. Build
+### 4. Build
```shell
dotnet build MySolution.sln
```
-The analyzer auto-packs on build. Consuming projects resolve the package from the local `.packages/` directory. Diagnostics and generated code appear automatically.
+The analyzer auto-packs on build. The version override file is generated automatically. Consuming projects resolve the package from the artifacts directory. Diagnostics and generated code appear automatically.
---
@@ -100,31 +97,33 @@ The analyzer auto-packs on build. Consuming projects resolve the package from th
### Auto-Pack on Build
-When `ScribeSolutionAnalyzer=true`, the SDK sets `GeneratePackageOnBuild=true` and redirects `PackageOutputPath` to the `.packages/` directory. Every build produces a fresh `.nupkg`.
+When `ScribeSolutionAnalyzer=true`, the SDK sets `GeneratePackageOnBuild=true` and redirects `PackageOutputPath` to `$(ArtifactsPath)packages/`. Every build produces a fresh `.nupkg`.
-### Fixed Version
+### Timestamp Version
-The version is locked to `0.0.0-local`. This avoids version management complexity — there's no NBGV, no timestamps, no override files. Consuming projects always reference `Version="0.0.0-local"`.
+Each build produces a unique version: `0.0.0-dev.yyyyMMddHHmmss` (e.g. `0.0.0-dev.20260406054112`). This serves two purposes:
-### Cache Invalidation
+1. **Forces Visual Studio to reload** — VS detects a new analyzer version and reloads the analyzer assembly, picking up changes immediately
+2. **Eliminates NuGet cache issues** — each version is unique, so NuGet never serves stale cached content
-NuGet caches extracted packages in the global packages folder (`~/.nuget/packages/`). With a fixed version, the cache would serve stale content. The SDK handles this automatically: before each pack, the `_ScribeSolutionAnalyzerClearCache` target removes the cached extraction and the old `.nupkg`, forcing NuGet to re-extract the freshly-built package on the next restore.
+### Version Override File
-### Package Directory
+After each pack, the SDK generates a `.Directory.Packages.targets` file in the artifacts directory:
-By default, packages are placed in `.packages/` relative to the solution directory. The SDK resolves the location in this order:
+```xml
+
+
+
+
+
+
+```
-1. `$(ScribeSolutionPackagesDir)` — if explicitly set by the developer
-2. `$(SolutionDir).packages\` — when building via a solution file or from Visual Studio
-3. `$(MSBuildProjectDirectory)\..\.packages\` — fallback for individual project builds
+This is the same mechanism as [LocalDev](project-setup.md#local-development-localdev). Consuming projects import the file via wildcard from `$(ArtifactsPath)`, which updates the `PackageVersion` to match the freshly-built package.
-Override the default in your `Directory.Build.props`:
+### Package Directory
-```xml
-
- $(MSBuildThisFileDirectory).packages\
-
-```
+Packages are placed in `$(ArtifactsPath)packages/` — the standard artifacts packages directory. This requires the solution to use artifact output (`UseArtifactsOutput=true`), which is the recommended setup for modern .NET projects.
---
@@ -135,16 +134,19 @@ A typical solution with a solution-local analyzer:
```
MySolution/
MySolution.sln
- NuGet.config ← registers .packages/ as a source
- Directory.Build.props ← (optional) shared properties
- .gitignore ← includes .packages/
- .packages/ ← auto-generated, git-ignored
- MyAnalyzer.0.0.0-local.nupkg
+ Directory.Build.props ← UseArtifactsOutput, RestoreAdditionalProjectSources
+ Directory.Build.targets ← Import override files from .artifacts/
+ Directory.Packages.props ← PackageVersion Include for MyAnalyzer
+ .gitignore ← includes .artifacts/
+ .artifacts/ ← auto-generated, git-ignored
+ packages/
+ MyAnalyzer.0.0.0-dev.20260406054112.nupkg
+ MyAnalyzer.Directory.Packages.targets ← version override file
MyAnalyzer/
MyAnalyzer.csproj ← ScribeSolutionAnalyzer=true
MyGenerator.cs
MyApp/
- MyApp.csproj ←
+ MyApp.csproj ←
Program.cs
```
@@ -152,7 +154,7 @@ MySolution/
## Multiple Analyzers
-A solution can have multiple solution-local analyzers. All pack to the same `.packages/` directory:
+A solution can have multiple solution-local analyzers. Each generates its own override file in the artifacts directory:
```xml
@@ -174,30 +176,34 @@ A solution can have multiple solution-local analyzers. All pack to the same `.pa
```
-```xml
-
-
-
-
-
+The wildcard import in `Directory.Build.targets` picks up all override files:
+
+```
+.artifacts/
+ FirstAnalyzer.Directory.Packages.targets
+ SecondAnalyzer.Directory.Packages.targets
+ packages/
+ FirstAnalyzer.0.0.0-dev.20260406054112.nupkg
+ SecondAnalyzer.0.0.0-dev.20260406054112.nupkg
```
---
## Relationship to LocalDev
-Solution-local analyzers and LocalDev solve different problems:
+Solution-local analyzers use the **same mechanism** as LocalDev (version override files + NuGet source registration) but scoped to a single solution:
| Concern | LocalDev | Solution-Local Analyzer |
|---------|----------|------------------------|
| Scope | Cross-repo (e.g. Scribe → Hermetic → MyApp) | Intra-solution |
| Trigger | `.localscribe` sentinel file | `ScribeSolutionAnalyzer=true` property |
| Published? | Yes (eventually to NuGet) | Never |
-| Version management | NBGV + timestamp + override files | Fixed `0.0.0-local` |
-| Package directory | Shared `/.artifacts/packages/` | Solution-local `.packages/` |
-| Setup complexity | `$(ScribeRoot)`, trigger project, package names | One property + NuGet source |
+| Version management | NBGV + timestamp + override files | Timestamp-only + override files |
+| Package directory | Shared `$(ScribeRoot)/.artifacts/packages/` | Solution `$(ArtifactsPath)packages/` |
+| Override file location | `$(ScribeRoot)/.artifacts/` | `$(ArtifactsPath)` |
+| Setup complexity | `$(ScribeRoot)`, trigger project, package names | One property + consumer-side imports |
-If your analyzer is (or will become) a standalone package consumed across repositories, use the standard Scribe SDK workflow with [LocalDev](project-setup.md#local-development-localdev). If it exists solely to serve the solution it lives in, use `ScribeSolutionAnalyzer`.
+Both features are independent and can coexist. A solution can have solution-local analyzers and also participate in a LocalDev chain.
---
@@ -206,11 +212,10 @@ If your analyzer is (or will become) a standalone package consumed across reposi
| Property | Value | Set by |
|----------|-------|--------|
| `$(ScribeSolutionAnalyzer)` | `true` | Developer (in `.csproj`) |
-| `$(ScribeSolutionPackagesDir)` | `.packages/` at solution root | SDK (overridable) |
-| `$(Version)` | `0.0.0-local` | SDK |
-| `$(PackageVersion)` | `0.0.0-local` | SDK |
+| `$(Version)` | `0.0.0-dev.yyyyMMddHHmmss` | SDK (timestamp-based) |
+| `$(PackageVersion)` | `0.0.0-dev.yyyyMMddHHmmss` | SDK (same as Version) |
| `$(GeneratePackageOnBuild)` | `true` | SDK |
-| `$(PackageOutputPath)` | `$(ScribeSolutionPackagesDir)` | SDK |
+| `$(PackageOutputPath)` | `$(ArtifactsPath)packages\` | SDK |
---
@@ -219,10 +224,10 @@ If your analyzer is (or will become) a standalone package consumed across reposi
| Decision | Choice | Rationale |
|----------|--------|-----------|
| Detection mechanism | Explicit `ScribeSolutionAnalyzer=true` property | Simple, discoverable, no magic. Convention-based detection would be ambiguous. |
-| Version strategy | Fixed `0.0.0-local` | Avoids version management complexity. Cache invalidation is handled by a build target. |
-| Package directory | `.packages/` at solution root | Mirrors `.artifacts/` convention. Solution-scoped, git-ignored. |
-| NuGet source registration | Manual one-time setup (NuGet.config or Directory.Build.props) | The SDK can only configure projects that use it. Consuming projects need a standard NuGet mechanism. |
-| Cache invalidation | Delete from NuGet global cache before each pack | Ensures consuming projects always get the fresh package without requiring unique versions. |
+| Version strategy | Timestamp-based `0.0.0-dev.yyyyMMddHHmmss` | Unique version per build forces Visual Studio to reload analyzers. Eliminates NuGet cache issues. |
+| Package directory | `$(ArtifactsPath)packages/` | Uses the solution's existing artifact output directory. No separate `.packages/` directory needed. |
+| Override files | Same mechanism as LocalDev | Proven pattern — `PackageVersion Update` entries in auto-generated `.Directory.Packages.targets` files, imported via wildcard. |
+| Consumer-side setup | Manual one-time setup (`Directory.Build.props` + `.targets`) | Same pattern as LocalDev. All projects in the solution share the same `Directory.Build.*` files, so one-time setup covers everything. |
---
@@ -230,25 +235,26 @@ If your analyzer is (or will become) a standalone package consumed across reposi
### Consuming project doesn't see the analyzer
-1. Verify the `.packages/` directory contains a `.nupkg` for the analyzer
-2. Verify `NuGet.config` (or `RestoreAdditionalProjectSources`) includes the `.packages/` directory
-3. Run `dotnet restore` on the consuming project
-4. In Visual Studio, try **Build → Rebuild Solution**
+1. Verify `$(ArtifactsPath)packages/` contains a `.nupkg` for the analyzer
+2. Verify `Directory.Build.props` registers `$(ArtifactsPath)packages` in `RestoreAdditionalProjectSources`
+3. Verify `Directory.Build.targets` imports `$(ArtifactsPath)*.Directory.Packages.targets`
+4. Run `dotnet restore` on the consuming project
+5. In Visual Studio, try **Build → Rebuild Solution**
### Stale generated code after analyzer changes
-The `_ScribeSolutionAnalyzerClearCache` target should handle this automatically. If you still see stale output:
+Each build produces a unique timestamp version, which forces Visual Studio to reload the analyzer. If you still see stale output:
-1. Delete the `.packages/` directory
-2. Delete `~/.nuget/packages//0.0.0-local/`
-3. Run `dotnet restore && dotnet build`
+1. Close and reopen the solution in Visual Studio
+2. Run `dotnet restore && dotnet build`
-### Package directory location is wrong
+### ArtifactsPath is not set
-Set `$(ScribeSolutionPackagesDir)` explicitly in your `Directory.Build.props`:
+The solution must use artifact output. Add to `Directory.Build.props`:
```xml
- $(MSBuildThisFileDirectory).packages\
+ true
+ $(MSBuildThisFileDirectory).artifacts/
```
From c22453b9df38e8fdad5efdd7cafd6f4a8392b9f5 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 6 Apr 2026 06:31:45 +0000
Subject: [PATCH 4/5] docs(scribe): mention solution-local analyzers in SDK
package description
Agent-Logs-Url: https://github.com/BulletsForHumanity/Scribe/sessions/65805b99-d304-4fa6-aa9e-c36044fb2184
Co-authored-by: crazycrank <11194014+crazycrank@users.noreply.github.com>
---
Scribe.Sdk/Scribe.Sdk.csproj | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/Scribe.Sdk/Scribe.Sdk.csproj b/Scribe.Sdk/Scribe.Sdk.csproj
index 2146d66..741a423 100644
--- a/Scribe.Sdk/Scribe.Sdk.csproj
+++ b/Scribe.Sdk/Scribe.Sdk.csproj
@@ -3,7 +3,8 @@
BulletsForHumanity.Scribe.Sdk — MSBuild SDK for Roslyn analyzer/generator projects.
This is a pack-only project. It produces no build output — the NuGet package
- contains only MSBuild props/targets, polyfill stubs, and LocalDev infrastructure.
+ contains only MSBuild props/targets, polyfill stubs, Solution-Local Analyzer
+ infrastructure, and LocalDev infrastructure.
-->
BulletsForHumanity.Scribe.Sdk
@@ -18,7 +19,7 @@
$(NoWarn);NU5128;NU5129;NETSDK1212
true
- MSBuild SDK for Roslyn analyzer and source generator projects. Provides default project configuration, analyzer packaging targets, netstandard2.0 polyfill stubs, and LocalDev multi-repo development infrastructure. Use with: <Project Sdk="BulletsForHumanity.Scribe.Sdk">
+ MSBuild SDK for Roslyn analyzer and source generator projects. Provides default project configuration, analyzer packaging targets, netstandard2.0 polyfill stubs, solution-local analyzer auto-pack support, and LocalDev multi-repo development infrastructure. Use with: <Project Sdk="BulletsForHumanity.Scribe.Sdk">
roslyn;analyzer;generator;source-generator;msbuild-sdk;scribe
From 60484f32ee5a4365afa1ebdbe748d724209e0da3 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 6 Apr 2026 07:12:11 +0000
Subject: [PATCH 5/5] fix(scribe): normalise ArtifactsPath separator and prefer
NuGetPackageVersion
Agent-Logs-Url: https://github.com/BulletsForHumanity/Scribe/sessions/3989b1e3-e54b-418a-ad37-33ad71a09acf
Co-authored-by: crazycrank <11194014+crazycrank@users.noreply.github.com>
---
Scribe/build/Scribe.SolutionAnalyzer.props | 17 +++++++++++++----
Scribe/build/Scribe.SolutionAnalyzer.targets | 8 +++++---
docs/architecture-infrastructure.md | 7 ++++---
3 files changed, 22 insertions(+), 10 deletions(-)
diff --git a/Scribe/build/Scribe.SolutionAnalyzer.props b/Scribe/build/Scribe.SolutionAnalyzer.props
index b941d47..5f7b01e 100644
--- a/Scribe/build/Scribe.SolutionAnalyzer.props
+++ b/Scribe/build/Scribe.SolutionAnalyzer.props
@@ -66,14 +66,23 @@
false
+
+
+
+ <_ScribeSaArtifactsDir>$([MSBuild]::EnsureTrailingSlash('$(ArtifactsPath)'))
+
+
-
- $(ArtifactsPath)packages\
+
+ $(_ScribeSaArtifactsDir)packages\
@@ -82,9 +91,9 @@
analyzer project itself (so it can resolve sibling solution-local packages).
Consuming projects must register this source in Directory.Build.props.
-->
-
+
- $(RestoreAdditionalProjectSources);$(ArtifactsPath)packages
+ $(RestoreAdditionalProjectSources);$(_ScribeSaArtifactsDir)packages
diff --git a/Scribe/build/Scribe.SolutionAnalyzer.targets b/Scribe/build/Scribe.SolutionAnalyzer.targets
index 211ee1d..8e83133 100644
--- a/Scribe/build/Scribe.SolutionAnalyzer.targets
+++ b/Scribe/build/Scribe.SolutionAnalyzer.targets
@@ -28,8 +28,10 @@
and '$(ArtifactsPath)' != ''"
>
- <_ScribeSaVersion>$(PackageVersion)
- <_ScribeSaOverridePath>$(ArtifactsPath)$(PackageId).Directory.Packages.targets
+ <_ScribeSaVersion>$(NuGetPackageVersion)
+ <_ScribeSaVersion Condition="'$(_ScribeSaVersion)' == ''">$(PackageVersion)
+ <_ScribeSaArtifactsDir>$([MSBuild]::EnsureTrailingSlash('$(ArtifactsPath)'))
+ <_ScribeSaOverridePath>$(_ScribeSaArtifactsDir)$(PackageId).Directory.Packages.targets
@@ -41,7 +43,7 @@
<_ScribeSaLine Include="</Project>" />
-
+