/crap-analysis
Analyze code coverage and CRAP (Change Risk Anti-Patterns) scores to identify high-risk code. Use OpenCover format with ReportGenerator for Risk Hotspots showing cyclomatic complexity and untested code paths.
$ npx -y skills add aaronontheweb/dotnet-skills --skill crap-analysis --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/crap-analysis
Context preview
The summary Claude sees to decide when to auto-load this skill.
Analyze code coverage and CRAP (Change Risk Anti-Patterns) scores to identify high-risk code. Use OpenCover format with ReportGenerator for Risk Hotspots showing cyclomatic complexity and untested code paths.
SKILL.md
crap-analysis.SKILL.mdname: crap-analysis
description: Analyze code coverage and CRAP (Change Risk Anti-Patterns) scores to identify high-risk code. Use OpenCover format with ReportGenerator for Risk Hotspots showing cyclomatic complexity and untested code paths.
invocable: true
CRAP Score Analysis
When to Use This Skill
Use this skill when:
- Evaluating code quality and test coverage before changes
- Identifying high-risk code that needs refactoring or testing
- Setting up coverage collection for a .NET project
- Prioritizing which code to test based on risk
- Establishing coverage thresholds for CI/CD pipelines
---
What is CRAP?
**CRAP Score = Complexity x (1 - Coverage)^2**
The CRAP (Change Risk Anti-Patterns) score combines cyclomatic complexity with test coverage to identify risky code.
| CRAP Score | Risk Level | Action Required | |------------|------------|-----------------| | **< 5** | Low | Well-tested, maintainable code | | **5-30** | Medium | Acceptable but watch complexity | | **> 30** | High | Needs tests or refactoring |
Why CRAP Matters
- **High complexity + low coverage = danger**: Code that's hard to understand AND untested is risky to modify
- **Complexity alone isn't enough**: A complex method with 100% coverage is safer than a simple method with 0%
- **Focuses effort**: Prioritize testing on complex code, not simple getters/setters
CRAP Score Examples
| Method | Complexity | Coverage | Calculation | CRAP | |--------|------------|----------|-------------|------| | `GetUserId()` | 1 | 0% | 1 x (1 - 0)^2 | **1** | | `ParseToken()` | 54 | 52% | 54 x (1 - 0.52)^2 | **12.4** | | `ValidateForm()` | 20 | 0% | 20 x (1 - 0)^2 | **20** | | `ProcessOrder()` | 45 | 20% | 45 x (1 - 0.20)^2 | **28.8** | | `ImportData()` | 80 | 10% | 80 x (1 - 0.10)^2 | **64.8** |
---
Coverage Collection Setup
coverage.runsettings
Create a `coverage.runsettings` file in your repository root. The **OpenCover format is required** for CRAP score calculation because it includes cyclomatic complexity metrics.
<?xml version="1.0" encoding="utf-8" ?>
<RunSettings>
<DataCollectionRunSettings>
<DataCollectors>
<DataCollector friendlyName="XPlat code coverage">
<Configuration>
<!-- OpenCover format includes cyclomatic complexity for CRAP scores -->
<Format>cobertura,opencover</Format>
<!-- Exclude test and benchmark assemblies -->
<Exclude>[*.Tests]*,[*.Benchmark]*,[*.Migrations]*</Exclude>
<!-- Exclude generated code, obsolete members, and explicit exclusions -->
<ExcludeByAttribute>Obsolete,GeneratedCodeAttribute,CompilerGeneratedAttribute,ExcludeFromCodeCoverageAttribute</ExcludeByAttribute>
<!-- Exclude source-generated files, Blazor generated code, and migrations -->
<ExcludeByFile>**/obj/**/*,**/*.g.cs,**/*.designer.cs,**/*.razor.g.cs,**/*.razor.css.g.cs,**/Migrations/**/*</ExcludeByFile>
<!-- Exclude test projects -->
<IncludeTestAssembly>false</IncludeTestAssembly>
<!-- Optimization flags -->
<SingleHit>false</SingleHit>
<UseSourceLink>true</UseSourceLink>
<SkipAutoProps>true</SkipAutoProps>
</Configuration>
</DataCollector>
</DataCollectors>
</DataCollectionRunSettings>
</RunSettings>Key Configuration Options
| Option | Purpose | |--------|---------| | `Format` | Must include `opencover` for complexity metrics | | `Exclude` | Exclude test/benchmark assemblies by pattern | | `ExcludeByAttribute` | Skip generated, obsolete, and explicitly excluded code (includes `ExcludeFromCodeCoverageAttribute`) | | `ExcludeByFile` | Skip source-generated files, Blazor components, and migrations | | `SkipAutoProps` | Don't count auto-properties as branches |
---
ReportGenerator Installation
Install ReportGenerator as a local tool for generating HTML reports with Risk Hotspots.
Add to .config/dotnet-tools.json
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-reportgenerator-globaltool": {
"version": "5.4.5",
"commands": ["reportgenerator"],
"rollForward": false
}
}
}Then restore:
dotnet tool restore
Or Install Globally
dotnet tool install --global dotnet-reportgenerator-globaltool
---
Collecting Coverage
Run Tests with Coverage Collection
# Clean previous results
rm -rf coverage/ TestResults/
# Run unit tests with coverage
dotnet test tests/MyApp.Tests.Unit \
--settings coverage.runsettings \
--collect:"XPlat Code Coverage" \
--results-directory ./TestResults
# Run integration tests (optional, adds to coverage)
dotnet test tests/MyApp.Tests.Integration \
--settings coverage.runsettings \
--collect:"XPlat Code Coverage" \
--results-directory ./TestResults
Generate HTML Report
dotnet reportgenerator \
-reports:"TestResults/**/coverage.opencover.xml" \
-targetdir:"coverage" \
-reporttypes:"Html;TextSummary;MarkdownSummaryGithub"
Report Types
| Type | Description | Output | |------|-------------|--------| | `Html` | Full interactive report | `coverage/index.html` | | `TextSummary` | Plain text summary | `coverage/Summary.txt` | | `MarkdownSummaryGithub` | GitHub-compatible markdown | `coverage/SummaryGithub.md` | | `Badges` | SVG badges for README | `coverage/badge_*.svg` | | `Cobertura` | Merged Cobertura XML | `coverage/Cobertura.xml` |
---
Reading the Report
Risk Hotspots Section
The HTML report includes a **Risk Hotspots** section showing methods sorted by complexity:
- **Cyclomatic Complexity**: Number of independent paths through code (if/else, switch cases, loops)
- **NPath Complexity**: Number of acyclic execution paths (exponential growth with nesting)
- **Crap Score**: Calculated from complexity and coverage
Interpreting Results
Risk Hotspots
─────────────
Method Complexity Coverage
Read more
name: crap-analysis description: Analyze code coverage and CRAP (Change Risk Anti-Patterns) scores to identify high-risk code. Use OpenCover format with ReportGenerator for Risk Hotspots showing cyclomatic complexity and untested code paths. invocable: true
CRAP Score Analysis
When to Use This Skill
Use this skill when:
- Evaluating code quality and test coverage before changes
- Identifying high-risk code that needs refactoring or testing
- Setting up coverage collection for a .NET project
- Prioritizing which code to test based on risk
- Establishing coverage thresholds for CI/CD pipelines
---
What is CRAP?
**CRAP Score = Complexity x (1 - Coverage)^2**
The CRAP (Change Risk Anti-Patterns) score combines cyclomatic complexity with test coverage to identify risky code.
| CRAP Score | Risk Level | Action Required | |------------|------------|-----------------| | **< 5** | Low | Well-tested, maintainable code | | **5-30** | Medium | Acceptable but watch complexity | | **> 30** | High | Needs tests or refactoring |
Why CRAP Matters
- **High complexity + low coverage = danger**: Code that's hard to understand AND untested is risky to modify
- **Complexity alone isn't enough**: A complex method with 100% coverage is safer than a simple method with 0%
- **Focuses effort**: Prioritize testing on complex code, not simple getters/setters
CRAP Score Examples
| Method | Complexity | Coverage | Calculation | CRAP | |--------|------------|----------|-------------|------| | `GetUserId()` | 1 | 0% | 1 x (1 - 0)^2 | **1** | | `ParseToken()` | 54 | 52% | 54 x (1 - 0.52)^2 | **12.4** | | `ValidateForm()` | 20 | 0% | 20 x (1 - 0)^2 | **20** | | `ProcessOrder()` | 45 | 20% | 45 x (1 - 0.20)^2 | **28.8** | | `ImportData()` | 80 | 10% | 80 x (1 - 0.10)^2 | **64.8** |
---
Coverage Collection Setup
coverage.runsettings
Create a `coverage.runsettings` file in your repository root. The **OpenCover format is required** for CRAP score calculation because it includes cyclomatic complexity metrics.
<?xml version="1.0" encoding="utf-8" ?>
<RunSettings>
<DataCollectionRunSettings>
<DataCollectors>
<DataCollector friendlyName="XPlat code coverage">
<Configuration>
<!-- OpenCover format includes cyclomatic complexity for CRAP scores -->
<Format>cobertura,opencover</Format>
<!-- Exclude test and benchmark assemblies -->
<Exclude>[*.Tests]*,[*.Benchmark]*,[*.Migrations]*</Exclude>
<!-- Exclude generated code, obsolete members, and explicit exclusions -->
<ExcludeByAttribute>Obsolete,GeneratedCodeAttribute,CompilerGeneratedAttribute,ExcludeFromCodeCoverageAttribute</ExcludeByAttribute>
<!-- Exclude source-generated files, Blazor generated code, and migrations -->
<ExcludeByFile>**/obj/**/*,**/*.g.cs,**/*.designer.cs,**/*.razor.g.cs,**/*.razor.css.g.cs,**/Migrations/**/*</ExcludeByFile>
<!-- Exclude test projects -->
<IncludeTestAssembly>false</IncludeTestAssembly>
<!-- Optimization flags -->
<SingleHit>false</SingleHit>
<UseSourceLink>true</UseSourceLink>
<SkipAutoProps>true</SkipAutoProps>
</Configuration>
</DataCollector>
</DataCollectors>
</DataCollectionRunSettings>
</RunSettings>Key Configuration Options
| Option | Purpose | |--------|---------| | `Format` | Must include `opencover` for complexity metrics | | `Exclude` | Exclude test/benchmark assemblies by pattern | | `ExcludeByAttribute` | Skip generated, obsolete, and explicitly excluded code (includes `ExcludeFromCodeCoverageAttribute`) | | `ExcludeByFile` | Skip source-generated files, Blazor components, and migrations | | `SkipAutoProps` | Don't count auto-properties as branches |
---
ReportGenerator Installation
Install ReportGenerator as a local tool for generating HTML reports with Risk Hotspots.
Add to .config/dotnet-tools.json
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-reportgenerator-globaltool": {
"version": "5.4.5",
"commands": ["reportgenerator"],
"rollForward": false
}
}
}Then restore:
dotnet tool restore
Or Install Globally
dotnet tool install --global dotnet-reportgenerator-globaltool
---
Collecting Coverage
Run Tests with Coverage Collection
# Clean previous results rm -rf coverage/ TestResults/ # Run unit tests with coverage dotnet test tests/MyApp.Tests.Unit \ --settings coverage.runsettings \ --collect:"XPlat Code Coverage" \ --results-directory ./TestResults # Run integration tests (optional, adds to coverage) dotnet test tests/MyApp.Tests.Integration \ --settings coverage.runsettings \ --collect:"XPlat Code Coverage" \ --results-directory ./TestResults
Generate HTML Report
dotnet reportgenerator \ -reports:"TestResults/**/coverage.opencover.xml" \ -targetdir:"coverage" \ -reporttypes:"Html;TextSummary;MarkdownSummaryGithub"
Report Types
| Type | Description | Output | |------|-------------|--------| | `Html` | Full interactive report | `coverage/index.html` | | `TextSummary` | Plain text summary | `coverage/Summary.txt` | | `MarkdownSummaryGithub` | GitHub-compatible markdown | `coverage/SummaryGithub.md` | | `Badges` | SVG badges for README | `coverage/badge_*.svg` | | `Cobertura` | Merged Cobertura XML | `coverage/Cobertura.xml` |
---
Reading the Report
Risk Hotspots Section
The HTML report includes a **Risk Hotspots** section showing methods sorted by complexity:
- **Cyclomatic Complexity**: Number of independent paths through code (if/else, switch cases, loops)
- **NPath Complexity**: Number of acyclic execution paths (exponential growth with nesting)
- **Crap Score**: Calculated from complexity and coverage
Interpreting Results
Risk Hotspots ───────────── Method Complexity Coverage
A comprehensive AI coding plugin with 30 skills and 5 specialized agents for professional .NET development. Battle-tested patterns from production systems covering C#, Akka.NET, Aspire, EF Core, testing, and performance optimization.
Other skills on dotnet-skills.
- /akka-aspire-configuration
Configure Akka.NET with .NET Aspire for local development and production deployments. Covers actor system setup, clustering, persistence, Akka.Management integration, and Aspire orchestration patterns.
Open skill - /akka-best-practices
Critical Akka.NET best practices including EventStream vs DistributedPubSub, supervision strategies, error handling, Props vs DependencyResolver, work distribution patterns, and cluster/local mode abstractions for testability.
Open skill - /akka-hosting-actor-patterns
Patterns for building entity actors with Akka.Hosting - GenericChildPerEntityParent, message extractors, cluster sharding abstraction, akka-reminders, and ITimeProvider. Supports both local testing and clustered production modes.
Open skill - /akka-management
Akka.Management for cluster bootstrapping, service discovery (Kubernetes, Azure, Config), health checks, and dynamic cluster formation without static seed nodes.
Open skill - /akka-testing-patterns
Write unit and integration tests for Akka.NET actors using modern Akka.Hosting.TestKit patterns. Covers dependency injection, TestProbes, persistence testing, and actor interaction verification. Includes guidance on when to use traditional TestKit.
Open skill - /aspire-configuration
Configure Aspire AppHost to emit explicit app config via environment variables; keep app code free of Aspire clients and service discovery.
Open skill

