Why this case study matters
dotnet/runtime is the canonical XML-shape monorepo at scale —
Microsoft’s CLR + Mono + NativeAOT runtime, the BCL (Base Class
Library: every System.* and Microsoft.Extensions.*), the per-OS
native libs, and the cross-arch installer. Built on the MSBuild +
Arcade SDK stack, with the actual build matrix orchestrated from
186 yml files under eng/pipelines/ (Azure DevOps), and only
20 yml under .github/workflows/ for GitHub-side automation.
Where apache/spark surfaced the xml_path_* rule-kind candidate via
49 pom.xml files (Maven), dotnet/runtime stress-tests it at one
order of magnitude bigger scale with:
- 1,091
.csprojfiles distributed acrosssrc/libraries/(902),src/mono/(59),src/tools/(30),src/native/(30),src/coreclr/(29),src/installer/(28),src/tasks/(15) - 234 solution files (231 .slnx + 3 vendored .sln)
- 280
.props+ 187.targetsfiles spread acrosseng/, per-area, and per-csproj overrides - 257
Directory.Build.{props,targets}files anchoring the parent-config inheritance chain - 76
.props/.targetsundereng/alone — the build-glue for the Arcade SDK integration, including the canonical 796-lineeng/Subsets.props“what to build” dispatch table
Total: ~2,300 distinct XML manifests in the sparse-checkout. The single most XML-heavy repo in the launch-evidence list.
Headline catch
Two v0.10 ship-targets get demand-validated by this case study.
1. xml_path_* is now a v0.10 ship-target
spark proved the shape via 49 pom.xml; dotnet/runtime proves the scale via ~2,300 distinct XML manifests across one repo. The cumulative drift exposure (8 distinct per-csproj invariants × 1,091 csprojs ≈ 8,700 invariant-instances that today are checked via regex fallback or not at all) is large enough to warrant the primitive’s design cost without further demand-source accumulation.
The 8 invariants the structured-query primitive unblocks per csproj:
| Invariant | Today (regex fallback) | With xml_path_* |
|---|---|---|
Root element is <Project> | (?m)^<Project(\s|>|/) (with BOM gymnastics) | xml_path_exists: $.Project |
| Sdk attribute is one of {NET.Sdk, NET.Sdk.Web, …} | Long alternation regex | xml_path_matches: $.Project[@Sdk] |
Has either <TargetFramework> or <TargetFrameworks> (mutually exclusive) | Counts substring matches; can’t enforce mutual exclusion | xml_path_count: ... == 1 |
TargetFrameworks references only known TFM variables from eng/Versions.props | Not expressible | Cross-file xml_path_matches |
<EnableNullable>true</EnableNullable> per area-level convention | Per-csproj regex check | xml_path_equals |
<RootNamespace> matches the file-system path | Not expressible | xml_path_equals against derived path |
<PackageReference Version="..." /> matches eng/Versions.props | Not expressible | Cross-file xml_path_equals |
Every <ProjectReference Include="..." /> resolves to an existing csproj | Not expressible | xml_path_resolves |
Promotes xml_path_* from “v0.11+ candidate” to “v0.10 ship-target”:
the structured-query family becomes complete (json/yaml/toml/xml) and
the two demand-driving repos are both flagship-visibility launches
(Apache TLP + Microsoft CLR).
2. dotnet@v1 (or csharp@v1) bundled ruleset is a v0.10 ship-target
alint ships rust@v1, java@v1, python@v1, node@v1, go@v1 —
but no C#/.NET equivalent today. dotnet/runtime is the first case
study where this gap is the dominant story.
A 12-rule dotnet@v1 ruleset (csproj-uses-net-sdk,
csproj-declares-target-framework, root-directory-build-{props,targets},
global-json + NuGet.config + Versions.props triad, the
bin/obj/artifacts/.vs hygiene set, msbuild-files-have-project-root)
would consolidate the build-system anchor + per-csproj XML-shape +
hygiene sections of this case study’s config into one extends: line.
Adopter surface is large: every Microsoft .NET project + every Azure SDK + every dotnet/* repo + microsoft/dapr + microsoft/orleans + the Unity scripting layer + a substantial fraction of enterprise codebases. Composition is mechanical; the 12 rules are already authored in this case study.
Where alint earns its keep here
dotnet/runtime is the flagship “XML-shape monorepo at scale” story for the launch:
- dotnet/runtime is Microsoft’s CLR + BCL. Importance-weighted larger than its star count — the runtime for every Azure SDK, every dotnet/* repo, every enterprise .NET deployment, the Unity scripting backend.
- No per-language linter sees the cross-csproj XML-shape. dotnet/format only sees C# AST; markdownlint only sees Markdown; the Azure DevOps build matrix evaluates the MSBuild graph but doesn’t enforce shape conventions on the manifests themselves. The invariants this case study enforces (every csproj is Sdk-style + has TargetFramework{s}, every area has its own Directory.Build.props, global.json + NuGet.config + Versions.props triad integrity, the bin/obj/artifacts/.vs hygiene set) are exactly the layer alint owns.
- The polyglot finding lands too. The .NET runtime is itself
polyglot (C# + C++ + native per-OS C + managed CMake glue), and the
per-area conventions
(
src/{coreclr,libraries,mono,installer,tasks,tools,samples}each with its own Directory.Build.props anchoring its own discipline) are exactly the cross-cutting structural layer alint exists to validate.
The case study also confirms a 9th source for cross_file_value_equals
via the dotnet-tools.json ↔ global.json Arcade SDK coherence
pattern — already past-saturation, already v0.10.
Future story angles
- Pair with the spark case study as the two-source demand
validation for
xml_path_*— spark for the proof-of-shape, dotnet for the proof-of-scale. dotnet@v1+csharp@v1ship narrative in the v0.10 release notes. dotnet/runtime is the clearest illustration of why the per-language ruleset matters; the adopter surface (every Microsoft .NET project, every Azure SDK) makes the shipping decision easy.azure-pipelines@v1as a v0.11+ candidate — the 186 yml undereng/pipelines/are out of scope forci/github-actions@v1today; worth tracking if a second Azure-Pipelines-driven repo lands in the evidence corpus.