Skip to content
Development
Skill

/crap-score

Calculates targeted CRAP (Change Risk Anti-Patterns) scores for a named .NET method, class, or single source file. Use when the user explicitly asks to compute CRAP scores or assess risky untested code for a specific target, combining Cobertura coverage data with cyclomatic

From plugin
dotnet-skills
5.1k96 skills16 agents
Install
$ npx -y skills add dotnet/skills --skill crap-score --agent claude-code

How 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-score

Context preview

The summary Claude sees to decide when to auto-load this skill.

Calculates targeted CRAP (Change Risk Anti-Patterns) scores for a named .NET method, class, or single source file. Use when the user explicitly asks to compute CRAP scores or assess risky untested code for a specific target, combining Cobertura coverage data with cyclomatic

SKILL.md

crap-score.SKILL.md
name: crap-score
description: >
  Calculates targeted CRAP (Change Risk Anti-Patterns) scores for a named .NET
  method, class, or single source file. Use when the user explicitly asks to
  compute CRAP scores or assess risky untested code for a specific target,
  combining Cobertura coverage data with cyclomatic complexity analysis.
  DO NOT USE FOR: project-wide coverage analysis, coverage plateau or "stuck
  coverage" diagnosis, what's blocking coverage, or where to add tests across
  a project (use coverage-analysis); writing tests; running tests without
  CRAP context.
license: MIT

CRAP Score Analysis

Calculate CRAP (Change Risk Anti-Patterns) scores for .NET methods to identify code that is both complex and undertested.

Background

The CRAP score combines **cyclomatic complexity** and **code coverage** into a single metric:

$$\text{CRAP}(m) = \text{comp}(m)^2 \times (1 - \text{cov}(m))^3 + \text{comp}(m)$$

Where:

  • $\text{comp}(m)$ = cyclomatic complexity of method $m$
  • $\text{cov}(m)$ = code coverage ratio (0.0 to 1.0) of method $m$

| CRAP Score | Risk Level | Interpretation | |------------|------------|----------------| | < 5 | Low | Simple and well-tested | | 5-15 | Moderate | Acceptable for most code | | 15-30 | High | Needs more tests or simplification | | > 30 | Critical | Refactor and add coverage urgently |

A method with 100% coverage has CRAP = complexity (the minimum). A method with 0% coverage has CRAP = complexity^2 + complexity.

When to Use

  • User wants to assess which methods are risky due to low coverage and high complexity
  • User asks for CRAP score of specific methods, classes, or files
  • User wants to prioritize which code to test next
  • User wants to evaluate test quality beyond simple coverage percentages

When Not to Use

  • User just wants to run tests (use `run-tests` skill)
  • User wants to write new tests (use `writing-mstest-tests` skill or general coding assistance)
  • User only wants a coverage percentage without complexity analysis

Inputs

| Input | Required | Description | |-------|----------|-------------| | Target scope | Yes | Method name, class name, or file path to analyze | | Test project path | No | Path to the test project. Defaults to discovering test projects in the solution. | | Source project path | No | Path to the source project under analysis |

Workflow

Step 1: Collect code coverage data

If no coverage data exists yet (no Cobertura XML available), **always run `dotnet test` with coverage collection first** and mention the exact command in your response. Do not skip this step -- CRAP scores require coverage data.

Check the test project's `.csproj` for the coverage package, then run the appropriate command:

| Coverage Package | Command | Output Location | |---|---|---| | `coverlet.collector` | `dotnet test --collect:"XPlat Code Coverage" --results-directory ./TestResults` | Typically under `TestResults/<guid>/coverage.cobertura.xml`. Search recursively under the results directory (for example, `TestResults/**/coverage.cobertura.xml`) or use any explicit coverage path the user provides. | | `Microsoft.Testing.Extensions.CodeCoverage` (.NET 9) | `dotnet test -- --coverage --coverage-output-format cobertura --coverage-output ./TestResults` | `--coverage-output` path | | `Microsoft.Testing.Extensions.CodeCoverage` (.NET 10+) | `dotnet test --coverage --coverage-output-format cobertura --coverage-output ./TestResults` | `--coverage-output` path |

Never estimate coverage

**Guessed coverage produces wrong CRAP scores, which is worse than no answer.** If the first command yields no Cobertura XML, work down this list before giving up:

1. Add a provider if none is referenced: `dotnet add <test.csproj> package coverlet.collector`, then re-run. 2. Use the standalone collector, which works even when the test host or a shared assembly blocks the in-proc collector: `dotnet tool install --global dotnet-coverage` then `dotnet-coverage collect -f cobertura -o coverage.cobertura.xml "dotnet test <test.csproj>"`. 3. Convert or summarize an existing report with ReportGenerator when only binary `.coverage` output exists: `dotnet tool install --global dotnet-reportgenerator-globaltool` then `reportgenerator -reports:<file> -targetdir:cov -reporttypes:Cobertura`. 4. Tests fail but still run? Coverage is collected from the tests that executed — continue with that data and note the failures.

If every path fails, **report that coverage could not be collected, show the commands you tried and their errors, and stop.** Report complexity on its own if useful, but never publish a CRAP number derived from an assumed coverage percentage.

Step 2: Compute cyclomatic complexity

Analyze the target source files to determine cyclomatic complexity per method. Count the following decision points (each adds 1 to the base complexity of 1):

| Construct | Example | |-----------|---------| | `if` | `if (x > 0)` | | `else if` | `else if (y < 0)` | | `case` (each) | `case 1:` | | `for` | `for (int i = 0; ...)` | | `foreach` | `foreach (var item in list)` | | `while` | `while (running)` | | `do...while` | `do { } while (cond)` | | `catch` (each) | `catch (Exception ex)` | | `&&` | `if (a && b)` | | `\|\|` (OR) | `if (a \|\| b)` | | `??` | `value ?? fallback` | | `?.` | `obj?.Method()` | | `? :` (ternary) | `x > 0 ? a : b` | | Pattern match arm | `x is > 0 and < 10` |

Base complexity is 1 for every method. Each decision point adds 1.

When analyzing, read the source file and count these constructs per method. Report the breakdown.

Step 3: Extract per-method coverage from Cobertura XML

Parse the Cobertura XML to find each method's `line-rate` attribute under the target `<class>` element. If `line-rate` is not available at method level, compute it from the `<lines>` elements:

$$\text{cov}(m) = \frac{\text{lines with hits} > 0}{\text{total lines}}$$

Method names in Cobertur

Read more
Ships withdotnet-skills

This repository contains the .NET team's curated set of core skills and custom agents for coding agents. For information about the Agent Skills standard, see agentskills.io. 📊 Dashboard - Accuracy and efficiency scoring trends for contained plugins (

Get the whole plugin

Other skills on dotnet-skills.