Architecture Validation and Code Quality - Garume/Manifold GitHub Wiki
Manifold enforces structural integrity and code quality through a multi-layered system of build-time checks, static analysis, formatting enforcement, and custom architecture validation scripts. These mechanisms operate in concert to ensure that every commit merged into the repository meets a consistent bar for correctness, style, and architectural compliance. This page documents each layer in detail, from the architecture.ps1 structural invariant checker through EditorConfig rules, MSBuild analyzer configuration, the warnings-as-errors policy, and deterministic build settings.
For an overview of the build scripts that orchestrate these checks, see Build System and Scripts. For the CI pipeline that runs them automatically, see CI/CD and Release Pipeline. For testing-specific quality enforcement (coverage thresholds), see Testing Strategy.
The quality.ps1 script orchestrates the full quality pipeline, executing each stage sequentially and halting on the first failure.
flowchart TD
A[quality.ps1] --> B[Restore Dependencies]
B --> C[Build Projects]
C --> D[Format Verification]
D --> E[Run Tests]
E --> F[Architecture Validation]
F --> G[All Checks Passed]
B -->|Failure| X[Exit with Error]
C -->|Failure| X
D -->|Failure| X
E -->|Failure| X
F -->|Failure| X
The pipeline follows a strict sequential order: restore, build, format check, test, and architecture validation. Each stage depends on the prior stage succeeding. The same pipeline runs in CI on every push and pull request.
Sources: build/quality.ps1:1-34
# quality.ps1 invocation chain
& (Join-Path $PSScriptRoot 'restore.ps1') -Solution $Solution
& (Join-Path $PSScriptRoot 'build.ps1') -Solution $Solution -NoRestore
& (Join-Path $PSScriptRoot 'format.ps1') -Solution $Solution -NoRestore
& (Join-Path $PSScriptRoot 'test.ps1') -Solution $Solution
& (Join-Path $PSScriptRoot 'architecture.ps1')Sources: build/quality.ps1:16-31
The architecture.ps1 script enforces two categories of structural invariants across the repository: required file presence and forbidden namespace references.
The script validates that a set of critical repository files exist at the expected paths:
| Required File | Purpose |
|---|---|
Manifold.slnx |
Solution file for all projects |
README.md |
Repository documentation |
LICENSE |
License file |
build/pack.ps1 |
NuGet packaging script |
.github/workflows/ci.yml |
CI workflow definition |
If any required file is missing, the script emits an error and fails the build.
Sources: build/architecture.ps1:7-20
The script scans all .cs, .csproj, .props, and .targets files under src/, tests/, and samples/ for references to legacy namespace identifiers that must not appear in the codebase:
| Forbidden Reference | Description |
|---|---|
DalamudMCP.Plugin |
Legacy plugin package |
DalamudMCP.Protocol |
Legacy protocol package |
DalamudMCP.Cli |
Legacy CLI package |
DalamudMCP.Framework |
Legacy framework package |
This prevents accidental reintroduction of dependencies on the predecessor project from which Manifold was extracted.
flowchart TD
A[architecture.ps1] --> B[Check Required Files]
B --> C{All Files Present?}
C -->|No| D[Collect Errors]
C -->|Yes| E[Scan Source Files]
E --> F[Check .cs .csproj .props .targets]
F --> G{Forbidden References?}
G -->|Yes| D
G -->|No| H[Architecture Checks Passed]
D --> I[Exit with Error Code 1]
$forbiddenReferences = @(
'DalamudMCP.Plugin',
'DalamudMCP.Protocol',
'DalamudMCP.Cli',
'DalamudMCP.Framework'
)
$sourceFiles = Get-ChildItem (Join-Path $root 'src'), (Join-Path $root 'tests'), (Join-Path $root 'samples') -Recurse -File |
Where-Object { $_.Extension -in '.cs', '.csproj', '.props', '.targets' }Sources: build/architecture.ps1:22-40
The .editorconfig file at the repository root defines formatting rules and diagnostic severities that apply to all C# files. These rules are enforced both by the IDE and at build time via the EnforceCodeStyleInBuild MSBuild property.
| Rule | Setting |
|---|---|
| Character set | UTF-8 |
| Line endings | CRLF |
| Indentation | 4 spaces |
| Final newline | Required |
| Trailing whitespace | Trimmed |
| Brace placement | New line before all braces |
| Namespace style | File-scoped (error) |
| Using placement | Outside namespace (error) |
| Accessibility modifiers | Always required (error) |
Sources: .editorconfig:1-24
The EditorConfig configures specific Roslyn analyzer diagnostics to error or none severity:
| Diagnostic ID | Description | Severity |
|---|---|---|
IDE0005 |
Remove unnecessary imports | Error |
IDE0161 |
Use file-scoped namespace | Error |
CA2211 |
Non-constant fields should not be visible | Error |
CA2227 |
Collection properties should be read only | Error |
CA2252 |
Opt-in to preview features | Error |
CA1707 |
Identifiers should not contain underscores | None (disabled) |
CA1716 |
Identifiers should not match keywords | None (disabled) |
CA1724 |
Type names should not match namespaces | None (disabled) |
CA1812 |
Avoid uninstantiated internal classes | None (disabled) |
CA2007 |
Do not directly await a Task | None (disabled) |
The disabled rules reflect deliberate design decisions. For example, CA1812 is suppressed because the source generator instantiates classes indirectly, and CA1707 is suppressed to allow underscored test method names.
Sources: .editorconfig:25-34
The format.ps1 script wraps dotnet format to verify or fix code style compliance:
$arguments = @('format', $Solution)
if (-not $Fix) {
$arguments += '--verify-no-changes'
}In verification mode (the default, used in CI), dotnet format checks all files against the EditorConfig rules and fails if any file differs from the expected format. The -Fix flag switches to auto-correction mode for local development.
Sources: build/format.ps1:1-32
The Directory.Build.props file at the repository root applies global build properties to every project in the solution. This centralizes quality enforcement so that individual project files do not need to repeat configuration.
flowchart TD
A[Directory.Build.props] --> B[Language Settings]
A --> C[Quality Enforcement]
A --> D[Build Determinism]
A --> E[Test Configuration]
B --> B1[".NET 10.0 / Latest C#"]
B --> B2["Nullable Enabled"]
B --> B3["Implicit Usings"]
C --> C1["Warnings as Errors"]
C --> C2["Code Style in Build"]
C --> C3[".NET Analyzers"]
C --> C4["Analysis Level: latest-recommended"]
D --> D1["Deterministic: true"]
D --> D2["Lock File Restore"]
D --> D3["Documentation File"]
E --> E1["Microsoft Testing Platform"]
E --> E2["coverlet.MTP"]
E --> E3["xunit.analyzers"]
| Property | Value | Purpose |
|---|---|---|
TargetFramework |
net10.0 |
Target .NET 10.0 runtime |
LangVersion |
latest |
Use latest C# language features |
ImplicitUsings |
enable |
Auto-import common namespaces |
Nullable |
enable |
Enable nullable reference types |
TreatWarningsAsErrors |
true |
Fail build on any warning |
WarningsAsErrors |
$(WarningsAsErrors);nullable |
Explicitly elevate nullable warnings |
EnforceCodeStyleInBuild |
true |
Apply EditorConfig during build |
AnalysisLevel |
latest-recommended |
Use latest recommended analyzer rules |
EnableNETAnalyzers |
true |
Enable .NET code analysis |
Deterministic |
true |
Ensure reproducible builds |
GenerateDocumentationFile |
true |
Require XML documentation |
RestorePackagesWithLockFile |
true |
Use NuGet lock files |
Sources: Directory.Build.props:1-16
The TreatWarningsAsErrors property set to true means that every compiler warning, analyzer warning, and code style warning is promoted to an error. This prevents warnings from accumulating in the codebase and ensures that developers address issues immediately. The additional WarningsAsErrors property with ;nullable explicitly includes nullable reference type warnings, ensuring that null safety violations are always treated as build failures.
The NoWarn property suppresses only CS1591 (missing XML documentation comments), allowing projects to build without requiring documentation on every public API member while still generating the documentation file.
Sources: Directory.Build.props:7-14
Projects that set IsManifoldTestProject to true receive additional configuration automatically:
- Output type set to
Exefor Microsoft Testing Platform compatibility -
UseMicrosoftTestingPlatformRunnerandTestingPlatformDotnetTestSupportenabled - Automatic package references:
coverlet.MTP(8.0.0) for code coverage andxunit.analyzers(1.27.0) for test quality - The shared
xunit.runner.jsonconfiguration is linked into the output directory
Sources: Directory.Build.props:18-33
The Directory.Build.targets file provides two post-build enforcement mechanisms:
Packable projects automatically include README.md and LICENSE in their NuGet packages, ensuring consistent package metadata across all published packages.
A custom MSBuild target FailIfCoverageThresholdMissing runs before the Test target and errors if any test project does not define a CoverageThreshold property. This guarantees that every test project has a coverage gate configured.
<Target Name="FailIfCoverageThresholdMissing"
BeforeTargets="Test"
Condition="'$(IsManifoldTestProject)' == 'true' and '$(CoverageThreshold)' == ''">
<Error Text="CoverageThreshold must be set for all test projects." />
</Target>Sources: Directory.Build.targets:1-18
The Manifold.Generators project has a specialized configuration because it targets netstandard2.0 (required for Roslyn analyzer compatibility) and operates as a Roslyn component rather than a standard library.
| Property | Value | Rationale |
|---|---|---|
IsRoslynComponent |
true |
Marks the project as a Roslyn analyzer/generator |
EnforceCodeStyleInBuild |
false |
Relaxed because netstandard2.0 has limited style support |
EnforceExtendedAnalyzerRules |
true |
Enables extended rules for analyzer authors |
NoWarn suppression |
RS2008 |
Suppresses analyzer release tracking diagnostic |
The project references Microsoft.CodeAnalysis.Analyzers (3.11.0) for analyzer best practices and Microsoft.CodeAnalysis.CSharp (4.14.0) for the Roslyn API.
Sources: src/Manifold.Generators/Manifold.Generators.csproj:1-29
Deterministic builds ensure that compiling the same source code with the same inputs always produces byte-identical output binaries. This is critical for:
- Reproducibility: Any build from the same commit produces the same artifacts
- Security: Build outputs can be verified against expected hashes
- Caching: Build systems can reliably cache compilation results
The Deterministic property in Directory.Build.props enables this for all projects. Combined with RestorePackagesWithLockFile, which pins exact NuGet package versions via packages.lock.json files, the entire build pipeline produces reproducible results.
flowchart TD
A[Deterministic Build] --> B["Deterministic: true"]
A --> C["RestorePackagesWithLockFile: true"]
A --> D["global.json SDK Pin"]
B --> E["Byte-identical Binaries"]
C --> F["Pinned NuGet Versions"]
D --> G["Fixed SDK Version 10.0.101"]
E --> H[Reproducible Artifacts]
F --> H
G --> H
Sources: Directory.Build.props:12-15
The CI workflow runs the full quality pipeline on every push and pull request:
jobs:
quality-and-test:
runs-on: windows-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-dotnet@v5
with:
global-json-file: global.json
cache: true
cache-dependency-path: |
**/packages.lock.json
- name: Quality
shell: pwsh
run: ./build/quality.ps1
- name: Pack
shell: pwsh
run: ./build/pack.ps1 -NoRestoreThe workflow uses packages.lock.json for NuGet cache keying and pins the SDK version via global.json. The quality.ps1 script executes all checks (build, format, test, architecture) as a single gate.
Sources: .github/workflows/ci.yml:1-24
flowchart TD
A[Code Quality Layers] --> B[Compile-Time]
A --> C[Build-Time]
A --> D[Script-Time]
A --> E[CI-Time]
B --> B1["Warnings as Errors"]
B --> B2["Nullable Analysis"]
B --> B3[".NET Analyzers"]
B --> B4["Source Generator Diagnostics"]
C --> C1["EditorConfig Enforcement"]
C --> C2["dotnet format Verification"]
C --> C3["Coverage Threshold Check"]
D --> D1["Required Files Check"]
D --> D2["Forbidden References Scan"]
E --> E1["quality.ps1 Pipeline"]
E --> E2["Pack Verification"]
| Layer | Mechanism | Failure Mode |
|---|---|---|
| Compile-time |
TreatWarningsAsErrors, nullable analysis, .NET analyzers |
Build error |
| Code style | EditorConfig rules with error severity | Build error |
| Format check | dotnet format --verify-no-changes |
Non-zero exit code |
| Test quality | xunit.analyzers, coverage threshold enforcement | Build/test error |
| Architecture | Required files check, forbidden reference scan | Script error |
| CI gate |
quality.ps1 orchestration on every push/PR |
Workflow failure |
- Build System and Scripts — Full documentation of the PowerShell build system
- CI/CD and Release Pipeline — GitHub Actions workflows that execute quality checks
- Testing Strategy — Test framework configuration and coverage thresholds
- Project Structure and Tech Stack — Repository layout and tooling choices
- Source Generator — Manifold.Generators — Source generator project with specialized analyzer configuration
- Diagnostics and Compile-Time Validation — Source generator diagnostic rules (DMCF001–DMCF005)
- Architecture Overview — High-level system architecture and package relationships