Novolis Docs
novolis-governance / platform-project-ref-mode.md

Platform ProjectReference mode

dotnetgovernancenovolis

Local multi-repo iteration without local NuGet feeds or committed cross-repo ProjectReferences.

When you open or build `Novolis.Platform` (the meta solution), MSBuild rewrites each project's existing Novolis.* PackageReference items into sibling ProjectReferences using a generated PackageId → .csproj map.

Committed .csproj files stay PackageReference-only. Per-repo solutions and CI stay on GitHub Packages.

Hard rules

RuleDetail
Intersect onlySubstitute only if the project has that PackageReference and the id is in the map and the path exists. Never invent ProjectReferences for unreferenced packages.
Map providersPackable projects only (IsPackable=true + PackageId). Tests/samples are consumers (they get substitution when they PackageReference Novolis packages).
No csproj dual-refNever hand-edit Package↔Project conditionals into .csproj.
No local feedsDo not use artifacts/nuget-local / novolis-local.
Prove doneStill publish to GPR for consumers outside meta mode.

Trigger

HowEffect
SolutionName == Novolis.PlatformAuto-enable
-p:NovolisUseProjectReferences=trueForce on (any solution / single project)
-p:NovolisUseProjectReferences=falseForce off (wins over SolutionName)
Env NOVOLIS_USE_PROJECT_REFERENCES=trueForce on when property unset
-p:NovolisLibraryRoot=PATHResolve mapped library projects from a selected checkout root, such as d:\novolis\novolis-lab\submodules; defaults to the workspace forest

Regenerate map + meta solution

dotnet run --project d:\novolis\novolis-tools\src\Novolis.Solution.Tool\Novolis.Solution.Tool.csproj --no-launch-profile -- generate --root d:\novolis
# or the thin PowerShell front door:
pwsh -File d:\novolis\novolis-governance\build\Generate-Platform-Slnx.ps1

Outputs:

Regenerate after adding/removing packable projects.

Daily use

# Open meta solution in VS / Rider, or:
dotnet build Novolis.Platform.slnx

# Single consumer against sibling source:
dotnet build path/to/Consumer.csproj -p:NovolisUseProjectReferences=true

# A lab with selected library submodules:
dotnet build path/to/Consumer.csproj -p:NovolisUseProjectReferences=true -p:NovolisLibraryRoot=path/to/submodules

Verify

dotnet run --project d:\novolis\novolis-tools\src\Novolis.Solution.Tool\Novolis.Solution.Tool.csproj --no-launch-profile -- verify --root d:\novolis
dotnet run --file d:\novolis\novolis-governance\scripts\verify-nuget-only.cs
pwsh -File d:\novolis\novolis-governance\scripts\gpr-health-check.ps1 -SkipRemote

novolis-solution verify checks map completeness and LibraryReference copy-drift.

Implementation files

FileRole
`Novolis.ProjectReferenceMode.props`Trigger + workspace root
`Novolis.ProjectReferenceMode.targets`Evaluation-time intersect substitution (required for NuGet static-graph restore)
`Novolis.LibraryReferenceBridge.props`Copy map → LibraryProjectMap
`Novolis.LibraryReference.targets`Expand LibraryReference after the map; default version 2026.1.*
`Novolis.Packaging.targets`Imports mode targets (all repos)
Novolis.Workspaces.DotNet.Slnx + novolis-solution generateMap + meta-solution generator

Stack analyzers (Novolis.StackAnalyzers.props) stay a separate analyzer ProjectReference (no PackageReference in csproj to substitute).

LibraryReference

Every repo that imports `Novolis.Packaging.targets` already expands LibraryReference. No per-repo copy of the targets, and no PackageReference to Novolis.MSBuild.LibraryReference inside the forest (that package is for consumers outside the workspace; its targets are not on disk for the first static-graph restore).

<ItemGroup>
  <LibraryReference Include="Novolis.Math.Geometry" />
</ItemGroup>

Governance copies @(NovolisPackageProject) into @(LibraryProjectMap), then expands the item: sibling .csproj exists → ProjectReference; missing → PackageReference at 2026.1.* (LibraryReferenceDefaultVersion). This does not depend on NovolisUseProjectReferences.

Existing PackageReference items still use ProjectReference mode. Leave that mode in place until those items are switched.

The targets under `build/libraryreference/` match the package in novolis-msbuild. CI uses the copy because it clones governance and not novolis-msbuild. A full workspace uses the live package files. verify-project-ref-mode.ps1 fails if the two copies drift.

Related