…tch]
buildTransitive/ is by definition the folder whose contents flow past the
project that referenced the package to everything downstream of it. The alias
props are 220 project-wide global usings keyed on the bare type name, so two
storage types reaching one project define every alias twice and the compile
dies with one CS1537 per quantity against GlobalUsings.g.cs.
That made the collision reachable from a project that references no alias
package at all. An application assembled from one project per storage type --
which is the shape the four packages exist to enable -- satisfies "one alias
package per project" in every project it contains, and still does not build.
Nothing in the 220 x (n-1) errors names a package, and the file they point at
is one the author never wrote.
build/ binds the aliases in the project that declares the reference and
nowhere else, which is what the documented rule already says. The transitive
explosion then cannot happen, rather than being diagnosed after the fact, and
the PrivateAssets="all" workaround stops being something a consumer has to
know in advance.
Verified end to end against the issue's repro -- two wrapper projects, one per
storage type, and an app that project-references both. Packed from this branch
it builds and each wrapper keeps its own binding; packed with the props back
in buildTransitive/ it fails with the CS1537 wall.
AliasPropsPackagingTests holds the folder, since it is one word in a
PackagePath that no build in this repository exercises -- nothing here consumes
the alias packages as packages -- and is only wrong once a consumer composes
two of them.
Fixes #244
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CGHNqRPzdBvCeegruQ11CG
Fixes #244
The change
The four
Semantics.Quantities.{Double,Float,Decimal,Precise}packages now pack their alias props intobuild/instead ofbuildTransitive/. This is suggested fix 1 in the issue — the one that removes the failure mode rather than diagnosing it.buildTransitive/is by definition the folder whose contents flow past the referencing project to everything downstream of it. The props are ~220 project-wide global usings keyed on the bare type name, so two storage types reaching one project define every alias twice and the compile dies with oneCS1537per quantity againstGlobalUsings.g.cs.That is what made the collision reachable from a project that references no alias package at all: the documented rule, one alias package per project, was satisfied by every project in the issue's repro and the build still failed.
build/binds the aliases in the project that declares the reference and nowhere else, which is what the rule already says.Establishing what motivated
buildTransitivefirstThe triage comment asked for this before committing to fix 1. Nothing depended on the transitive flow:
Semantics.TestandSemantics.Benchmarksboth referencektsu.PreciseNumberdirectly, each with a comment saying they do so rather than picking it up throughSemantics.Quantities.Precise, "which ships only props".Generate-AliasProps.ps1's own docstring described the intent as "a project that references the package can writeMass" —build/semantics, written against thebuildTransitive/folder.So
buildTransitive/looks like the conventional folder rather than a chosen behaviour, and fix 2 (the self-namingSEM010error) is not needed: its stated precondition, "ifbuildTransitivehas to stay", does not hold. A single project that directly references two alias packages is still aCS1537pile — but that project violates the documented rule itself, rather than inheriting a binding it never asked for. Happy to add the guard as a follow-up if you want that case named too.Verification
End to end, against the exact repro in the issue (
Foo.Storage.Double+Foo.Storage.Float, each referencing one alias package, andFoo.Appproject-referencing both and referencing no alias package):dotnet build Foo.AppbuildTransitive/(today'smain)CS1537linesbuild/(this branch)Foo.Appruns and printsMass\1[System.Double]andMass`1[System.Single]` — each wrapper keeps its own bindingAlso:
dotnet build Semantics.sln -c Release— 0 warnings, 0 errors.Semantics.Testonnet10.0— 1283 tests, 1275 passed, 8 skipped (Windows-only), 0 failed..nupkgfor Double and Float each contain exactlybuild/ktsu.Semantics.Quantities.<T>.props.verify-generatedgate stays clean. The generator script's output is byte-identical; only its destination directory changed, which is why the props show as pure renames in the diff.Test
Semantics.Test/Quantities/AliasPropsPackagingTests.cs, following the precedent ofGeneratorProjectReferenceTests— a condition cheap to state and cheap to check, so it is checked rather than left to a comment. Two tests assert each alias project packs exactly one props file, intobuild/, naming the file that exists on disk.Confirmed failing without the fix: reverting the folder move and the
PackagePathwhile keeping the tests produces 2 failures naming all four packages; restoring the fix returns the suite to 1283/0.The folder is one word in a
PackagePaththat no build in this repository exercises — nothing here consumes the alias packages as packages — and is only wrong once a consumer composes two of them. A regression should cost a test, not a release.Docs
Per suggested fix 3, the per-project scope is now documented where a consumer will meet it — the four package
READMEs, the four package<Description>s, andCLAUDE.md— including thatPrivateAssets="all"was the workaround for the old layout and is no longer needed.scripts/Generate-AliasProps.ps1and theverify-generated.ymlheader say why the folder isbuild/, so the next person does not "correct" it back.🤖 Generated with Claude Code
https://claude.ai/code/session_01CGHNqRPzdBvCeegruQ11CG
Generated by Claude Code