{
  "schema": "https://ai-atoms.com/schemas/skill-v1.json",
  "type": "skill",
  "id": "skill/crap-score",
  "version": "1.0.1",
  "name": "crap-score",
  "description": "Calculates CRAP (Change Risk Anti-Patterns) for a named .NET method, class, or file. USE FOR: explicit CRAP calculation or coverage-and-complexity risk within that named target, including which tests to prioritize. DO NOT USE FOR: project-wide coverage/CRAP, plateaus, or project-wide blockers/priorities (coverage-analysis); behavioral/pseudo-mutation gaps (test-gap-analysis); writing tests; test runs without CRAP context.",
  "system_prompt_fragment": "# CRAP Score Analysis\n\nCalculate CRAP (Change Risk Anti-Patterns) scores for .NET methods to identify code that is both complex and undertested.\n\n## Background\n\nThe CRAP score combines **cyclomatic complexity** and **code coverage** into a single metric:\n\n$$\\text{CRAP}(m) = \\text{comp}(m)^2 \\times (1 - \\text{cov}(m))^3 + \\text{comp}(m)$$\n\nWhere:\n\n- $\\text{comp}(m)$ = cyclomatic complexity of method $m$\n- $\\text{cov}(m)$ = code coverage ratio (0.0 to 1.0) of method $m$\n\n| CRAP Score | Risk Level | Interpretation |\n|------------|------------|----------------|\n| < 5        | Low        | Simple and well-tested |\n| 5-15       | Moderate   | Acceptable for most code |\n| 15-30      | High       | Needs more tests or simplification |\n| > 30       | Critical   | Refactor and add coverage urgently |\n\nA method with 100% coverage has CRAP = complexity (the minimum). A method with 0% coverage has CRAP = complexity^2 + complexity.\n\n## When to Use\n\n- User wants to assess which methods are risky due to low coverage and high complexity\n- User asks for CRAP score of specific methods, classes, or files\n- User wants to prioritize which code to test next\n- User wants to evaluate test quality beyond simple coverage percentages\n\n## When Not to Use\n\n- User just wants to run tests (use `run-tests` skill)\n- User wants to write new tests (use `writing-mstest-tests` skill or general coding assistance)\n- User only wants a coverage percentage without complexity analysis\n\n## Inputs\n\n| Input | Required | Description |\n|-------|----------|-------------|\n| Target scope | Yes | Method name, class name, or file path to analyze |\n| Test project path | No | Path to the test project. Defaults to discovering test projects in the solution. |\n| Source project path | No | Path to the source project under analysis |\n\n## Workflow\n\n### Step 1: Collect code coverage data\n\nIf 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.\n\nCheck the test project's `.csproj` for the coverage package, then run the appropriate command:\n\n| Coverage Package | Command | Output Location |\n|---|---|---|\n| `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. |\n| `Microsoft.Testing.Extensions.CodeCoverage` (.NET 9) | `dotnet test -- --coverage --coverage-output-format cobertura --coverage-output ./TestResults` | `--coverage-output` path |\n| `Microsoft.Testing.Extensions.CodeCoverage` (.NET 10+) | `dotnet test --coverage --coverage-output-format cobertura --coverage-output ./TestResults` | `--coverage-output` path |\n\n### Step 2: Compute cyclomatic complexity\n\nAnalyze the target source files to determine cyclomatic complexity per method. Count the following decision points (each adds 1 to the base complexity of 1):\n\n| Construct | Example |\n|-----------|---------|\n| `if` | `if (x > 0)` |\n| `else if` | `else if (y < 0)` |\n| `case` (each) | `case 1:` |\n| `for` | `for (int i = 0; ...)` |\n| `foreach` | `foreach (var item in list)` |\n| `while` | `while (running)` |\n| `do...while` | `do { } while (cond)` |\n| `catch` (each) | `catch (Exception ex)` |\n| `&&` | `if (a && b)` |\n| `\\|\\|` (OR) | `if (a \\|\\| b)` |\n| `??` | `value ?? fallback` |\n| `?.` | `obj?.Method()` |\n| `? :` (ternary) | `x > 0 ? a : b` |\n| Pattern match arm | `x is > 0 and < 10` |\n\nBase complexity is 1 for every method. Each decision point adds 1.\n\nWhen analyzing, read the source file and count these constructs per method. Report the breakdown.\n\n### Step 3: Extract per-method coverage from Cobertura XML\n\nParse 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:\n\n$$\\text{cov}(m) = \\frac{\\text{lines with hits} > 0}{\\text{total lines}}$$\n\nMethod names in Cobertura may differ from source (async methods, lambdas). Match by line ranges when names don't align.\n\n### Step 4: Calculate CRAP scores\n\nFor each method in scope, apply the formula:\n\n$$\\text{CRAP}(m) = \\text{comp}(m)^2 \\times (1 - \\text{cov}(m))^3 + \\text{comp}(m)$$\n\n### Step 5: Present results\n\nPresent a sorted table (highest CRAP first):\n\n```text\n| Method                          | Complexity | Coverage | CRAP Score | Risk     |\n|---------------------------------|------------|----------|------------|----------|\n| OrderService.ProcessOrder       | 12         | 45%      | 28.4       | High     |\n| OrderService.ValidateItems      | 8          | 90%      | 8.1        | Moderate |\n| OrderService.CalculateTotal     | 3          | 100%     | 3.0        | Low      |\n```\n\nInclude:\n\n- **Summary**: total methods analyzed, how many in each risk category\n- **Top offenders**: methods with CRAP > 30, with specific recommendations\n- **Quick wins**: methods with high complexity but where small coverage improvements would drop the score significantly\n\n### Step 6: Provide actionable recommendations\n\nFor high-CRAP methods, suggest one or both:\n\n1. **Add tests** -- identify uncovered branches and suggest specific test cases\n2. **Reduce complexity** -- suggest extract-method refactoring for deeply nested logic\n\nCalculate the **coverage needed** to bring a method below a CRAP threshold of 15:\n\n$$\\text{cov}_{\\text{needed}} = 1 - \\left(\\frac{15 - \\text{comp}}{\\text{comp}^2}\\right)^{1/3}$$\n\nThis formula only applies when comp < 15. When comp >= 15, the minimum possible CRAP score (at 100% coverage) is comp itself, which already meets or exceeds the threshold. In that case, **coverage alone cannot bring the CRAP score below the threshold** -- the method must be refactored to reduce its cyclomatic complexity first.\n\nReport this as: \"To bring `ProcessOrder` (complexity 12) below CRAP 15, increase coverage from 45% to at least 72%.\" For methods where complexity alone exceeds the threshold, report: \"`ComplexMethod` (complexity 18) cannot reach CRAP < 15 through testing alone -- reduce complexity by extracting sub-methods.\"\n\n## Validation\n\n- Verify that coverage data was collected successfully (Cobertura XML exists and contains data)\n- Cross-check that method names in coverage data match the source code\n- Confirm CRAP scores by spot-checking the formula on one method manually\n- Ensure a 100%-covered method's CRAP equals its complexity exactly\n\n## Common Pitfalls\n\n- **Stale coverage data**: Always regenerate coverage before computing CRAP scores. Old coverage files will produce misleading results.\n- **Method name mismatches**: Cobertura XML may use mangled/compiler-generated names for async methods, lambdas, or local functions. Match by line ranges when names don't align.\n- **Generated code**: Exclude auto-generated files (e.g., `*.Designer.cs`, `*.g.cs`) from analysis unless explicitly requested.",
  "applicable_domains": [
    "code",
    "dotnet",
    "engineering"
  ],
  "invocation": [
    "/crap-score"
  ],
  "tags": [
    "dotnet-test",
    "dotnet",
    "csharp",
    "microsoft"
  ],
  "authored_by": "anthropics",
  "source_url": "https://github.com/dotnet/skills/blob/main/plugins/dotnet-test/skills/crap-score/SKILL.md",
  "lifecycle": "stable",
  "category": "dotnet",
  "provenance": {
    "source": "dotnet/skills",
    "source_url": "https://github.com/dotnet/skills/blob/main/plugins/dotnet-test/skills/crap-score/SKILL.md",
    "author": "Microsoft / .NET Foundation",
    "license": "MIT",
    "notes": "Imported by scripts/import-anthropic-skills.py."
  }
}