agentsclimarketplace

Dotnet techne crap analysis

Skill Metalnib/dotnet-episteme-skills/skills/dotnet-techne-crap-analysis

DotNet Episteme Skills - a curated, manual-first .NET AI skills library rooted in systematic knowledge (episteme) and shaped by disciplined craft (techne), designed for engineers who prioritise precision over hype.

Install
npx -y skills add Metalnib/dotnet-episteme-skills --skill dotnet-techne-crap-analysis

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 11 stars11 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Use when you need CRAP score and coverage risk analysis to find high-risk code before refactoring or release. Keywords: CRAP score, risk hotspots, cyclomatic complexity, test coverage gaps, coverage threshold.

SKILL.md

11.5 KB, as published. Nobody here has run it

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 ScoreRisk LevelAction Required
< 5LowWell-tested, maintainable code
5-30MediumAcceptable but watch complexity
> 30HighNeeds 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

MethodComplexityCoverageCalculationCRAP
GetUserId()10%1 x (1 - 0)^21
ParseToken()5452%54 x (1 - 0.52)^212.4
ValidateForm()200%20 x (1 - 0)^220
ProcessOrder()4520%45 x (1 - 0.20)^228.8
ImportData()8010%80 x (1 - 0.10)^264.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

OptionPurpose
FormatMust include opencover for complexity metrics
ExcludeExclude test/benchmark assemblies by pattern
ExcludeByAttributeSkip generated, obsolete, and explicitly excluded code (includes ExcludeFromCodeCoverageAttribute)
ExcludeByFileSkip source-generated files, Blazor components, and migrations
SkipAutoPropsDon'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

TypeDescriptionOutput
HtmlFull interactive reportcoverage/index.html
TextSummaryPlain text summarycoverage/Summary.txt
MarkdownSummaryGithubGitHub-compatible markdowncoverage/SummaryGithub.md
BadgesSVG badges for READMEcoverage/badge_*.svg
CoberturaMerged Cobertura XMLcoverage/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  Crap Score
──────────────────────────────────────────────────────────────────
DataImporter.ParseRecord()      54          52%       12.4
AuthService.ValidateToken()     32          0%        32.0   ← HIGH RISK
OrderProcessor.Calculate()      28          85%       1.3
UserService.CreateUser()        15          100%      0.0

Action items:

  • ValidateToken() has CRAP > 30 with 0% coverage - test immediately or refactor
  • ParseRecord() is complex but has decent coverage - acceptable
  • CreateUser() and Calculate() are well-tested - safe to modify

Coverage Thresholds

Recommended Standards

Coverage TypeTargetAction
Line Coverage> 80%Good for most projects
Branch Coverage> 60%Catches conditional logic
CRAP Score< 30Maximum for new code

Configuring Thresholds

Create coverage.props in your repository:

<Project>
  <PropertyGroup>
    <!-- Coverage thresholds for CI enforcement -->
    <CoverageThresholdLine>80</CoverageThresholdLine>
    <CoverageThresholdBranch>60</CoverageThresholdBranch>
  </PropertyGroup>
</Project>

CI/CD Integration

GitHub Actions

name: Coverage

on:
  pull_request:
    branches: [main, dev]

jobs:
  coverage:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Setup .NET
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: '9.0.x'

      - name: Restore tools
        run: dotnet tool restore

      - name: Run tests with coverage
        run: |
          dotnet test \
            --settings coverage.runsettings \
            --collect:"XPlat Code Coverage" \
            --results-directory ./TestResults

      - name: Generate report
        run: |
          dotnet reportgenerator \
            -reports:"TestResults/**/coverage.opencover.xml" \
            -targetdir:"coverage" \
            -reporttypes:"Html;MarkdownSummaryGithub;Cobertura"

      - name: Upload coverage report
        uses: actions/upload-artifact@v4
        with:
          name: coverage-report
          path: coverage/

      - name: Add coverage to PR
        uses: marocchino/sticky-pull-request-comment@v2
        with:
          path: coverage/SummaryGithub.md

Azure Pipelines

- task: DotNetCoreCLI@2
  displayName: 'Run tests with coverage'
  inputs:
    command: 'test'
    arguments: '--settings coverage.runsettings --collect:"XPlat Code Coverage" --results-directory $(Build.SourcesDirectory)/TestResults'

- task: DotNetCoreCLI@2
  displayName: 'Generate coverage report'
  inputs:
    command: 'custom'
    custom: 'reportgenerator'
    arguments: '-reports:"$(Build.SourcesDirectory)/TestResults/**/coverage.opencover.xml" -targetdir:"$(Build.SourcesDirectory)/coverage" -reporttypes:"HtmlInline_AzurePipelines;Cobertura"'

- task: PublishCodeCoverageResults@2
  displayName: 'Publish coverage'
  inputs:
    codeCoverageTool: 'Cobertura'
    summaryFileLocation: '$(Build.SourcesDirectory)/coverage/Cobertura.xml'

Quick Reference

One-Liner Commands

# Full analysis workflow
rm -rf coverage/ TestResults/ && \
dotnet test --settings coverage.runsettings \
  --collect:"XPlat Code Coverage" \
  --results-directory ./TestResults && \
dotnet reportgenerator \
  -reports:"TestResults/**/coverage.opencover.xml" \
  -targetdir:"coverage" \
  -reporttypes:"Html;TextSummary"

# View summary
cat coverage/Summary.txt

# Open HTML report (Linux)
xdg-open coverage/index.html

# Open HTML report (macOS)
open coverage/index.html

# Open HTML report (Windows)
start coverage/index.html

Project Standards

MetricNew CodeLegacy Code
Line Coverage80%+60%+ (improve gradually)
Branch Coverage60%+40%+ (improve gradually)
Maximum CRAP30Document exceptions
High-risk methodsMust have testsAdd tests before modifying

What Gets Excluded

The recommended coverage.runsettings excludes:

PatternReason
[*.Tests]*Test assemblies aren't production code
[*.Benchmark]*Benchmark projects
[*.Migrations]*Database migrations (generated)
GeneratedCodeAttributeSource generators
CompilerGeneratedAttributeCompiler-generated code
ExcludeFromCodeCoverageAttributeExplicit developer opt-out
*.g.cs, *.designer.csGenerated files
*.razor.g.csBlazor component generated code
*.razor.css.g.csBlazor CSS isolation generated code
**/Migrations/**/*EF Core migrations (auto-generated)
SkipAutoPropsAuto-properties (trivial branches)

When to Update Thresholds

Lower thresholds temporarily for:

  • Legacy codebases being modernized (document in README)
  • Generated code that can't be modified
  • Third-party wrapper code

Never lower thresholds for:

  • "It's too hard to test" - refactor instead
  • "We'll add tests later" - add them now
  • New features - should meet standards from the start

Additional Resources

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.