This repository is the source for E128.Analyzers. The package holds Roslyn analyzers and code fixes that enforce opinionated .NET conventions at compile time.
dotnet add package E128.AnalyzersPaste this prompt into a coding agent in the repository you want to check:
Add the latest stable E128.Analyzers package to this repository. Resolve the version from nuget.org only, and write the exact version number. Never use a floating version.
Add one `<PackageReference Include="E128.Analyzers" PrivateAssets="all" />` to the root `Directory.Build.props`, inside an existing analyzer `ItemGroup` when one exists, in alphabetical position. Create the file if it is missing. Leave every other analyzer where it is.
If this repository uses Central Package Management, add one `PackageVersion` entry to `Directory.Packages.props`. Never duplicate a version entry. Otherwise set `Version` on the reference.
If `nuget.config` defines more than one package source, add a `packageSourceMapping` entry that maps `E128.*` to nuget.org.
In the root `.editorconfig`, under `[*.cs]`, set these categories to `error` and set `e128_max_complexity = linear`:
dotnet_analyzer_diagnostic.category-E128-Design.severity = error
dotnet_analyzer_diagnostic.category-E128-Maintainability.severity = error
dotnet_analyzer_diagnostic.category-E128-Performance.severity = error
dotnet_analyzer_diagnostic.category-E128-Reliability.severity = error
dotnet_analyzer_diagnostic.category-E128-Security.severity = error
dotnet_analyzer_diagnostic.category-E128-Style.severity = error
dotnet_analyzer_diagnostic.category-E128-Testing.severity = error
dotnet_analyzer_diagnostic.category-E128-Unsafe.severity = error
e128_max_complexity = linear
If the root `.editorconfig` does not exist, create it with `root = true`. Preserve the existing settings and every non-E128 analyzer severity.
Do not fix source findings, suppress or downgrade E128 rules, or change other analyzer severities.
Run the Release build of the solution at the repository root. Report the package version, the project count, and the build status. Report the warning and error totals, and the error count per E128 rule ID. Count each diagnostic once when the output repeats it. Leave every finding unfixed unless I approve a fix.
e128_max_complexity sets the algorithm level. linear is the strictest tier and reports linearithmic or worse code. Other tiers: linearithmic, quadratic, cubic, exponential.
- Analyzers:
src/E128.Analyzers/. The README lists every rule. - Code fixes:
src/E128.Analyzers.CodeFixers/ - Packaging: the package ships
E128.Analyzers.dllandE128.Analyzers.CodeFixers.dllunderanalyzers/dotnet/cs. - Tests:
tests/E128.Analyzers.Tests/ - Release notes:
AnalyzerReleases.Shipped.md - Publishing:
.github/workflows/publish.ymlpushes the package to nuget.org on each merge tomainthat changessrc/E128.Analyzers/orsrc/E128.Analyzers.CodeFixers/
Report an analyzer defect, or ask for a new rule, in the issues of this repository.
The rest of the repository is a .NET 11 reference application that runs the analyzers on its own code. It shows conventions for web, CLI, and container applications. The build uses deny-by-default code analysis with third-party Roslyn analyzers. Tests run on xUnit v3 with the Microsoft Testing Platform. ArchUnitNET tests check structural invariants. Central Package Management pins transitive versions, and Renovate updates dependencies. GitHub Actions publishes the package with OIDC trusted publishing.
The repository also includes a Claude Code development harness with bash scripts, rules, skills, and agents. It uses the Lode Coding Toolkit for structured project documentation.
# Build
scripts/build.sh
# Test (default: the CI category only)
scripts/test.sh
# Full CI pipeline (shell lint, format, build, and test)
scripts/ci.sh
# Podman
scripts/podman.sh build
scripts/podman.sh test| Component | Description |
|---|---|
| E128.Reference.Web | Minimal API web app with Kestrel and a health endpoint |
| E128.Reference.Cli | System.CommandLine CLI with a --name option |
| E128.Reference.Core | Shared library (Greeter service, models, repositories, services) |
| E128.Analyzers | Roslyn analyzers (E128001 to E128218), published to nuget.org |
| E128.Analyzers.CodeFixers | Code fix providers for the analyzer rules, shipped in the same package |
| E128.Reference.Tests | xUnit v3 and MTP with the CI, Podman, and Manual test categories |
| Architecture.Tests | ArchUnitNET structural invariant tests (layers, naming, sealed) |
| E128.Analyzers.Tests | Analyzer and code fix unit tests |
| E128.Reference.Cli.Tests | Unit tests for the CLI app |
| E128.Reference.Core.Tests | Unit tests for the Greeter, GreetingService, and repositories |
| E128.Benchmarks | BenchmarkDotNet starter benchmarks for Core, the Web endpoints, and analyzers |
| Podman | Hardened Resolute multi-stage Dockerfile and compose.yaml |
| Bash scripts | Build, test, format, CI, Podman, and lode tasks (catalog) |
| Claude Code harness | CLAUDE.md, rules, hooks, skills, and agents (see .claude/) |
| CI/CD | GitHub Actions CI and NuGet trusted publishing |
| Renovate | Automatic dependency updates with grouped PRs and a security bypass |
| Lode | Structured documentation with the Lode Coding Toolkit |
| Tool | Version | macOS | Ubuntu/Debian | Windows |
|---|---|---|---|---|
| .NET SDK | 11.0+ | brew install dotnet |
Microsoft docs | winget install Microsoft.DotNet.SDK.11 |
| bash | 4.0+ | brew install bash |
Included (5.1+) | WSL2 recommended |
| shellcheck | 0.11.0+ | brew install shellcheck |
sudo apt install shellcheck |
WSL2 recommended |
| ripgrep (rg) | 14+ | brew install ripgrep |
sudo apt install ripgrep |
winget install BurntSushi.ripgrep.MSVC |
| fd | 9+ | brew install fd |
sudo apt install fd-find |
winget install sharkdp.fd |
| jq | 1.7+ | brew install jq |
sudo apt install jq |
winget install jqlang.jq |
| Podman | 4+ | brew install podman |
sudo apt install podman |
winget install RedHat.Podman |
| Tool | Purpose | Install |
|---|---|---|
| dotnet-outdated | NuGet update checker | dotnet tool install -g dotnet-outdated-tool |
| jb (ReSharper CLI) | Semantic code cleanup (format.sh) |
dotnet tool install -g JetBrains.ReSharper.GlobalTools |
Note (jb):
scripts/format.shrunsjb cleanupcodebeforedotnet formatwhenjbis on$PATH, and skips that step when it is absent.--checkalways skipsjbbecausejbhas no verify-only mode.
Note (macOS): The default
/bin/bashon macOS is version 3.2. Install bash 5 or later with Homebrew and put/opt/homebrew/bin/bashbefore/bin/bashin$PATH.
Note (Ubuntu): The
fdpackage is namedfd-findand its binary isfdfind. Create an alias:ln -s $(which fdfind) ~/.local/bin/fd.
Note (macOS and Podman): Podman needs a Linux VM on macOS and does not bundle one. After install, run
podman machine init && podman machine start.
.
├── .claude/ # Claude Code harness
│ ├── agents/ # Agent definitions
│ ├── hooks/ # Session and guardrail hooks
│ ├── rules/ # Contextual rule files
│ ├── settings.json # Permissions and hook config
│ ├── skills/ # Skill directories
│ └── tmp/ # Session artifacts (gitignored)
├── .editorconfig # Code style (120 char, 4-space, file-scoped ns)
├── .globalconfig # Analyzer severities (deny-by-default)
├── .github/workflows/ci.yml # GitHub Actions CI
├── .github/workflows/publish.yml # NuGet trusted publishing
├── .gitleaks.toml # Secret scan config
├── .mcp.json # Claude Code MCP server config (Roslyn Navigator)
├── .opencode/ # opencode mirror of the Claude Code harness
│ ├── agent-assets/ # Generated agent asset copies
│ ├── agents/ # Generated agent definitions
│ └── plugins/ # opencode plugins (guardrail parity with .claude/hooks)
├── AGENTS.md # Cross-harness AI instructions
├── CLAUDE.md # Always-loaded AI instructions
├── Directory.Build.props # Shared build properties
├── Directory.Build.targets # Conditional targets (test project config)
├── Directory.Packages.props # Central package versions
├── Dockerfile # Multi-stage web app image
├── compose.yaml # Container orchestration
├── E128.Reference.slnx # Solution file
├── global.json # SDK version and MTP test runner config
├── benchmarks/
│ └── E128.Benchmarks/ # BenchmarkDotNet starter benchmarks
├── docs/ # Architecture and guide documents
├── dotnet-tools.json # Repository-local .NET tool manifest
├── lode/ # Project knowledge documentation
├── nuget.config # Single source, source mapping, and 72-hour publish age
├── opencode.json # opencode v2 config: model, instructions, MCP, permissions, policies
├── plans/ # Structured planning documents
├── prompts/ # System prompt for Lode sessions
├── renovate.json # Renovate dependency update config
├── scripts/ # Bash development scripts ([catalog](scripts/README.md))
├── src/
│ ├── E128.Analyzers/ # Roslyn analyzers (published NuGet package)
│ ├── E128.Analyzers.CodeFixers/ # Code fix providers (second DLL in the package)
│ ├── E128.Reference.Core/ # Shared library
│ ├── E128.Reference.Web/ # ASP.NET Core minimal API
│ └── E128.Reference.Cli/ # System.CommandLine CLI
└── tests/
├── Architecture.Tests/ # ArchUnitNET structural invariants
├── E128.Analyzers.Tests/ # Analyzer unit tests
├── E128.Reference.Cli.Tests/ # CLI unit tests
├── E128.Reference.Core.Tests/# Core library unit tests
└── E128.Reference.Tests/ # Web integration tests (xUnit v3 and MTP)
The repository uses a deny-by-default analyzer strategy:
dotnet_analyzer_diagnostic.severity = errormakes each diagnostic an error unless a rule overrides it- Third-party analyzer packages (see
Directory.Packages.props) add more than 1000 rules .globalconfigsets every off-by-default rule toerror, and itsNot Enabledsection lists each rule left belowerrorwith a reason- Test projects use a separate
tests/.globalconfigatglobal_level = 101for test overrides
| Package | Focus |
|---|---|
| AsyncFixer | Async and await patterns |
| IDisposableAnalyzers | Resource ownership |
| Meziantou.Analyzer | General best practices |
| Microsoft.VisualStudio.Threading.Analyzers | Threading correctness |
| Roslynator.Analyzers | Code style and quality |
| Roslynator.CodeAnalysis.Analyzers | Advanced code analysis |
| Roslynator.Formatting.Analyzers | Formatting consistency |
| SharpSource | Common pitfalls |
| SonarAnalyzer.CSharp | Security and reliability |
Tests run on xUnit v3 with the Microsoft Testing Platform (MTP) runner:
# Run CI tests (default category)
scripts/test.sh
# Run one test class (bare or fully qualified name)
scripts/test.sh E128.Reference.Core.Tests.GreeterTests
# Run all tests, including Podman and Manual
scripts/test.sh --all
# Verbose output (human-readable)
scripts/test.sh --verboseTest categories:
[Trait("Category", "CI")]: runs in the CI pipeline. Each test must be fast and deterministic.[Trait("Category", "Podman")]: builds and tests the container image. Podman must be installed.[Trait("Category", "Manual")]: needs an external dependency or a manual setup step.
# Build, run, and test (scripts/podman.sh)
scripts/podman.sh build
scripts/podman.sh run
scripts/podman.sh test
scripts/podman.sh stop
# Or use podman compose directly
podman compose up -d
curl http://localhost:8080/health
# → Healthy
podman compose downGitHub Actions (.github/workflows/ci.yml) runs on a pull request to main, and on manual dispatch. A push to main does not repeat the suite. A concurrency group cancels an in-flight run when the same pull request receives a new push.
Two jobs run in parallel:
- Format + Build + Test: format check (
dotnet format --verify-no-changes), a Release build with ReadyToRun disabled, a Debug build that feeds coverage, CI-category tests with coverage, and an 80% line and assembly coverage gate - Lint + Scan:
scripts/lint-shell.shruns shellcheck over every tracked shell script. The job then checks that the opencode agent mirror is current, verifies every guardrail hook referenced by the plugin exists, and runs a gitleaks scan over the full git history
NuGet publishing (.github/workflows/publish.yml) starts on a push to main that changes src/E128.Analyzers/ or src/E128.Analyzers.CodeFixers/. The workflow uses OIDC trusted publishing, so it needs no API key. It skips the push when the version already exists on nuget.org.
The dotnet-overhaul skill is a portable .NET overhaul loop for Claude Code.
It modernizes language usage and enforces strict code analysis.
It also reviews performance, concurrency, and security, and it verifies that all tests pass.
Copy it into any .NET repository and run it there.
Opinionated. The skill enforces fixed conventions: deny-by-default analyzers, immutability, the MTP test runner, and strict analysis. Edit conventions.md after you copy the skill to match your project.
Iterative. For a first large overhaul, run the skill several times. Approve a subset of findings per run, commit, then run again. After the codebase is clean, run the skill periodically to catch drift from new code, updated analyzers, or a TFM upgrade.
# Run this from inside your target repository
cp -r /path/to/dotnet-reference/.claude/skills/dotnet-overhaul .claude/skills/The skill includes its own scripts, pattern files, and conventions template. It needs no other file from this repository.
Create a branch first, because the skill changes many files across the codebase:
git checkout -b refactor/dotnet-overhaul
# Then in Claude Code
/dotnet-overhaul
/dotnet-overhaul src/MyProjectAfter you copy the skill, you can edit these files in .claude/skills/dotnet-overhaul/:
| File | Purpose |
|---|---|
conventions.md |
Project coding standards, analyzer overrides, auto-approved fixes, and test relaxations |
lessons/*.md |
Known false positives and compiler edge cases found during overhaul runs |
The skill detects the test framework, the solution format, and the analyzer configuration.
The external agents (build-validator, sme-researcher, tdd-loop-optimizer) are optional, so the skill works without them.
Issues and pull requests are welcome. For a large change, open an issue first to discuss the approach.