---
name: debtmap
description: Analyze tech debt with debtmap, fix the top priority item, test, and commit
---

# Fix Top Priority Tech Debt

Use debtmap to analyze the repository and identify tech debt, then fix the highest priority item.

## Process

### Step 1: Verify Coverage Data
Check that the LCOV coverage data was generated by the workflow:
```
ls -la target/coverage/lcov.info
```
- The workflow has already run `cargo build` and `cargo tarpaulin` 
- Verify the file `target/coverage/lcov.info` exists
- **CRITICAL**: If the file is missing, STOP - something went wrong in the workflow
- The baseline coverage will be recorded from debtmap's output in Step 2

### Step 2: Initial Analysis
The workflow has already generated the debtmap analysis. Extract the baseline data:
```bash
# Verify the workflow-generated file exists
ls -la .prodigy/debtmap-before.json
```
- **CRITICAL**: Extract and record the BASELINE values from the workflow-generated JSON:
  ```bash
  # Extract key metrics
  TOTAL_DEBT_SCORE=$(jq '.total_debt_score' .prodigy/debtmap-before.json)
  OVERALL_COVERAGE=$(jq '.overall_coverage' .prodigy/debtmap-before.json)
  echo "BASELINE - Total Debt Score: $TOTAL_DEBT_SCORE"
  echo "BASELINE - Overall Coverage: $OVERALL_COVERAGE%"
  ```
- Store these as your BASELINE values for comparison later
- Extract the top recommendation details from JSON:
  ```bash
  # Get top priority item details
  jq '.items[0] | {
    score: .unified_score.final_score,
    location: .location,
    action: .recommendation.primary_action,
    rationale: .recommendation.rationale,
    expected_impact: .expected_impact
  }' .prodigy/debtmap-before.json
  ```

### Step 3: Identify Priority
The debtmap JSON output provides the #1 TOP RECOMMENDATION with structured data:

Extract the key information from the JSON:
```bash
# Get detailed top priority information
jq '.items[0] | {
  unified_score: .unified_score.final_score,
  location: {
    file: .location.file,
    function: .location.function,
    line: .location.line
  },
  debt_type: .debt_type,
  action: .recommendation.primary_action,
  rationale: .recommendation.rationale,
  implementation_steps: .recommendation.implementation_steps,
  expected_impact: {
    coverage_improvement: .expected_impact.coverage_improvement,
    complexity_reduction: .expected_impact.complexity_reduction,
    risk_reduction: .expected_impact.risk_reduction
  },
  coverage_info: {
    current_coverage: .transitive_coverage.direct,
    uncovered_lines: .transitive_coverage.uncovered_lines
  }
}' .prodigy/debtmap-before.json
```

Priority categories (based on unified_score):
1. **CRITICAL (Score >100)**: High complexity with poor coverage
2. **HIGH (Score 50-100)**: Important business logic with test gaps
3. **MEDIUM (Score 20-50)**: Moderate complexity or coverage issues
4. **LOW (Score <20)**: Minor improvements

### Step 3.5: Evaluate Refactoring Approach (for Complexity Issues)

When debtmap identifies a function with high complexity (>10), evaluate different refactoring strategies:

**CRITICAL: Before extracting helper methods, consider these approaches in order:**

1. **Static Pure Function Extraction (PREFERRED)**
   - Extract classification/decision logic as static pure functions
   - These functions should be reusable across the codebase
   - Example: `classify_type(name: &str) -> Type` instead of multiple helpers
   - Benefits: Testable in isolation, reduces main function complexity, functional style

2. **Pattern Consolidation**
   - Look for repeated patterns in conditionals
   - Use match expressions with guards instead of if-else chains
   - Combine similar branches using pattern matching
   - Example: Replace multiple if-else checking string patterns with a single match

3. **Functional Composition**
   - Use `.map()`, `.filter()`, `.fold()` instead of loops
   - Chain operations instead of intermediate variables
   - Extract predicates as pure functions

**WARNING: Avoid these anti-patterns:**
- Creating multiple single-use helper methods that are only called from tests
- Extracting helpers that aren't used in production code paths
- Over-engineering: 5+ helper methods for a 15-line function
- Adding complexity to reduce complexity

**Validation Check:**
Before implementing, verify your approach will:
- Actually reduce the complexity score (not just move it)
- Keep or improve test coverage (not decrease it)
- Result in fewer total functions (or same number with better structure)
- Use functional programming patterns where appropriate

### Step 3.7: Quick Refactoring Decision Tree

For functions with complexity > 10:

```
Is it a visitor pattern or large switch/match?
├─ YES → Don't refactor, add tests if needed
└─ NO → Continue
   │
   Does it classify/categorize inputs?
   ├─ YES → Extract as pure static function
   └─ NO → Continue
      │
      Does it have repeated similar conditions?
      ├─ YES → Consolidate with pattern matching
      └─ NO → Continue
         │
         Does it have nested loops?
         ├─ YES → Convert to iterator chains
         └─ NO → Consider if refactoring is needed
```

### Step 4: Plan the Fix
Based on the ACTION specified in the top recommendation:

**For "Refactor to reduce complexity" actions:**

1. **Analyze the function structure:**
   - Is it mostly a large switch/match on different cases? → Keep as-is, it's already functional
   - Is it performing classification/categorization? → Extract as pure static function
   - Is it orchestrating I/O operations? → Extract business logic only
   - Is it a visitor pattern implementation? → Consider if complexity is inherent

2. **Choose refactoring strategy based on complexity source:**
   
   **Pattern: Multiple similar conditionals**
   ```rust
   // Before: Complexity 15
   if name.contains("async") || name.contains("await") { 
       CallType::Async 
   } else if name.starts_with("handle_") { 
       CallType::Delegate 
   } else if name.starts_with("map") { 
       CallType::Pipeline 
   } else { 
       CallType::Direct 
   }
   
   // After: Extract as pure function
   fn classify_call_type(name: &str) -> CallType {
       match () {
           _ if name.contains("async") || name.contains("await") => CallType::Async,
           _ if name.starts_with("handle_") => CallType::Delegate,
           _ if name.starts_with("map") => CallType::Pipeline,
           _ => CallType::Direct,
       }
   }
   ```

   **Pattern: Nested loops and conditions**
   - Replace with iterator chains and functional combinators
   - Extract predicates as named functions

   **Pattern: Large match/switch statement**
   - Often this is the CORRECT pattern - don't refactor
   - Cyclomatic complexity ≠ bad code
   - Visitor patterns naturally have high complexity

3. **Test the refactoring impact:**
   - Count functions before: `find src -name "*.rs" | xargs grep -E "^\s*(pub\s+)?fn\s+" | wc -l`
   - Apply refactoring
   - Count functions after
   - If function count increased by >20%, reconsider approach

**For "Add X unit tests" actions:**
- First, assess if the function is orchestration or I/O code
- If it's an orchestration/I/O function:
  - Extract any pure business logic into separate testable functions
  - Move formatting/parsing logic to dedicated modules
  - Keep thin I/O wrappers untested (they're not the real debt)
- For actual business logic functions:
  - Plan test cases for:
    - Happy path scenarios
    - Edge cases and boundary conditions
    - Error conditions and invalid inputs
    - Any uncovered branches or paths

### Step 5: Implement the Fix
Apply the planned changes:

**For Orchestration and I/O Functions:**
- Extract pure logic into testable functions:
  - Move formatting logic to pure functions that return strings
  - Extract parsing/validation to separate modules
  - Create pure functions for decision logic (e.g., "should_generate_report")
- Keep I/O operations in thin wrappers that call the pure functions
- Write tests for the extracted pure functions, not the I/O wrappers
- Consider moving business logic to appropriate modules (e.g., `parsers`, `formatters`, `validators`)

**For Testing Business Logic:**
- Write comprehensive test cases
- Ensure all identified scenarios are covered
- Use descriptive test names
- Follow existing test patterns in the codebase

**For Refactoring:**
- Apply functional programming patterns
- Maintain backwards compatibility
- Preserve all existing functionality
- Keep changes focused and incremental

### Step 6: Verify Changes
Run the following commands in order:
```
just ci
```
- All tests must pass
- No clippy warnings allowed
- Code must be properly formatted

### Step 7: Regenerate Coverage
If you added tests, regenerate coverage:
```
cargo tarpaulin --config .tarpaulin.toml --out Lcov --output-dir target/coverage --timeout 120
```
- This will update the lcov.info file for the final analysis
- Use the same tarpaulin config as the workflow for consistency

### Step 8: Final Analysis
Run debtmap again to verify improvement:
```
target/release/debtmap analyze . --lcov target/coverage/lcov.info --output .prodigy/debtmap-final.json --format json
```
- **CRITICAL**: Extract and record the NEW values from JSON:
  ```bash
  # Extract final metrics
  NEW_DEBT_SCORE=$(jq '.total_debt_score' .prodigy/debtmap-final.json)
  NEW_COVERAGE=$(jq '.overall_coverage' .prodigy/debtmap-final.json)
  echo "FINAL - Total Debt Score: $NEW_DEBT_SCORE"
  echo "FINAL - Overall Coverage: $NEW_COVERAGE%"

  # Calculate changes
  echo "Coverage change: $(echo "$NEW_COVERAGE - $OVERALL_COVERAGE" | bc -l)%"
  echo "Debt score change: $(echo "$TOTAL_DEBT_SCORE - $NEW_DEBT_SCORE" | bc -l)"
  ```
- Verify the original issue is resolved:
  ```bash
  # Check if the original issue is still #1 priority
  jq '.items[0] | {
    score: .unified_score.final_score,
    location: .location,
    action: .recommendation.primary_action
  }' .prodigy/debtmap-final.json
  ```

### Step 8.5: Understanding Metrics Changes

**Interpret your metrics changes:**

**Debt Score Changes:**
- **Decreased**: The refactoring/tests successfully reduced technical debt
- **Increased slightly (<100 points)**: Often due to test functions being counted (they have 0% coverage)
- **Increased significantly (>100 points)**: May indicate added complexity without corresponding benefit

**Coverage Changes:**
- **Increased**: Tests are covering more production code
- **Decreased slightly (<1%)**: Usually because new test functions aren't covered by other tests
- **Decreased significantly (>1%)**: New production code may lack tests

**Function Count Changes:**
- **Same or slight decrease**: Good refactoring that consolidated logic
- **Increase <10**: Acceptable for better code structure
- **Increase >20**: Consider if the refactoring added too much abstraction

**Understanding Trade-offs:**
- Test functions add to debt score (limitation of the tool)
- Extracting pure functions may temporarily increase metrics but improve maintainability
- Focus on whether the specific issue was resolved, not just raw metrics
- Consider long-term maintainability over short-term metrics

### Step 9: Commit Changes
Create a descriptive commit message using the values extracted from debtmap JSON:

**For test additions:**
```bash
# Extract values for commit message
ORIGINAL_FUNCTION=$(jq -r '.items[0].location.function' .prodigy/debtmap-before.json)
ORIGINAL_SCORE=$(jq -r '.items[0].unified_score.final_score' .prodigy/debtmap-before.json)
ORIGINAL_COVERAGE=$(jq -r '.items[0].transitive_coverage.direct' .prodigy/debtmap-before.json)

# Create commit with extracted values
git commit -m "test: add comprehensive tests for $ORIGINAL_FUNCTION

- Added [number] test cases covering [specific scenarios]
- Coverage: $(printf "%.2f" $(echo "$NEW_COVERAGE - $OVERALL_COVERAGE" | bc -l))% (from $(printf "%.2f" $OVERALL_COVERAGE)% to $(printf "%.2f" $NEW_COVERAGE)%)
- Debt score: $(printf "%.0f" $(echo "$TOTAL_DEBT_SCORE - $NEW_DEBT_SCORE" | bc -l)) (from $(printf "%.0f" $TOTAL_DEBT_SCORE) to $(printf "%.0f" $NEW_DEBT_SCORE))
- Resolved: Priority $(printf "%.1f" $ORIGINAL_SCORE) - $ORIGINAL_FUNCTION with $(printf "%.0f%%" $(echo "$ORIGINAL_COVERAGE * 100" | bc -l)) coverage"
```

**For complexity reduction:**
```bash
# Extract complexity information
ORIGINAL_COMPLEXITY=$(jq -r '.items[0].cyclomatic_complexity' .prodigy/debtmap-before.json)

git commit -m "refactor: reduce complexity in $ORIGINAL_FUNCTION

- [Specific refactoring applied, e.g., \"Replaced nested loops with iterator chain\"]
- Complexity reduced from $ORIGINAL_COMPLEXITY to [new_complexity]
- Coverage: $(printf "%.2f" $(echo "$NEW_COVERAGE - $OVERALL_COVERAGE" | bc -l))% (from $(printf "%.2f" $OVERALL_COVERAGE)% to $(printf "%.2f" $NEW_COVERAGE)%)
- Debt score: $(printf "%.0f" $(echo "$TOTAL_DEBT_SCORE - $NEW_DEBT_SCORE" | bc -l)) (from $(printf "%.0f" $TOTAL_DEBT_SCORE) to $(printf "%.0f" $NEW_DEBT_SCORE))
- Resolved: Priority $(printf "%.1f" $ORIGINAL_SCORE) - $ORIGINAL_FUNCTION complexity $ORIGINAL_COMPLEXITY"
```

**Important**: Use the exact coverage percentages and debt scores extracted from debtmap JSON output.

## Important Instructions

**IMPORTANT**: When making ANY commits, do NOT include attribution text like "🤖 Generated with Claude Code" or "Co-Authored-By: Claude" in commit messages. Keep commits clean and focused on the actual changes.

**COMMIT MESSAGE REQUIREMENTS**:
Every commit MUST include:
1. What was changed (refactoring or tests added)
2. **Coverage change with actual percentages from debtmap JSON** (extracted using `jq '.overall_coverage'`)
3. **Debt score change with actual values from debtmap JSON** (extracted using `jq '.total_debt_score'`)
4. The priority score and description of resolved issue (extracted from `.items[0]`)

**Note**: Always use the JSON output values from debtmap, not the line coverage from tarpaulin directly. The workflow generates `.prodigy/debtmap-before.json` (baseline) and `.prodigy/debtmap-after.json` (final state for validation). You can also create `.prodigy/debtmap-final.json` for manual comparison if needed.

## Success Criteria

Complete each step in order:
- [ ] Coverage data verified from workflow-generated lcov.info (must exist)
- [ ] Initial debtmap analysis completed with top priority identified
- [ ] Implementation plan created based on the ACTION specified
- [ ] Fix implemented following the plan
- [ ] All tests passing (cargo nextest run)
- [ ] No clippy warnings (cargo clippy)
- [ ] Code properly formatted (cargo fmt)
- [ ] Coverage regenerated if tests were added
- [ ] Final debtmap analysis shows improvement
- [ ] Changes committed with descriptive message

## Notes

- Always work on one issue at a time for focused, measurable improvements
- The unified priority score considers complexity, coverage, and risk factors
- Priority score 10.0 indicates critical issues requiring immediate attention
- Complexity refactoring should preserve all existing functionality
- Each commit should resolve the identified priority issue

## Orchestration and I/O Function Guidelines

When debtmap flags orchestration or I/O functions as untested:

1. **Recognize the pattern**: Functions with cyclomatic complexity = 1 that coordinate modules or perform I/O are not the real debt
2. **Extract testable logic**: Instead of testing I/O directly, extract pure functions that can be unit tested
3. **Follow functional programming principles**: 
   - Pure core: Business logic in pure functions
   - Imperative shell: Thin orchestration/I/O wrappers that don't need testing
4. **Common patterns to extract**:
   - Formatting functions: Extract logic that builds strings from data
   - Parsing functions: Move to dedicated parser modules
   - Decision functions: Extract "should we do X" logic from "do X" execution
   - Coordination logic: Extract "how to coordinate" from "perform coordination"
5. **Don't force unit tests on**: 
   - Functions that just print to stdout
   - Simple delegation to other modules
   - Module orchestration that just sequences calls
   - File I/O wrappers
   - Network I/O operations

## Common Pitfalls to Avoid

### When Refactoring for Complexity:

❌ **DON'T:**
- Extract helper methods that are only called once
- Create test-only helper functions (helpers not used in production code)
- Break apart a clear match/switch into multiple functions
- Add abstraction layers for simple logic
- Refactor visitor pattern implementations (they're meant to have many branches)
- Create 5+ helper methods for a 15-line function

✅ **DO:**
- Extract reusable classification/decision logic as static pure functions
- Use functional patterns (map, filter, fold) where appropriate
- Consolidate similar patterns into single functions
- Keep related logic together
- Accept that some functions legitimately have high complexity
- Test the extracted pure functions thoroughly

### Understanding Debt Score Impact:

**Why debt score might increase after adding tests:**
- Test functions are counted in metrics but have 0% coverage themselves
- Each test function adds ~5-10 points to debt score
- This is a current limitation of the debtmap tool

**What to focus on:**
- Whether the TARGET function's complexity was reduced
- Whether the specific issue identified was resolved
- Overall code quality and maintainability
- Whether the refactoring follows functional programming principles

### Functional Programming Preferences:

**Prefer these patterns:**
- Pure functions over stateful methods
- Static methods for classification/utility functions
- Match expressions with guards over if-else chains
- Iterator chains over imperative loops
- Function composition over deep nesting
- Immutability by default

**Example of good refactoring:**
```rust
// Extract classification logic as pure static function
impl MyStruct {
    // This can be tested in isolation and reused
    fn classify_item(name: &str) -> ItemType {
        match () {
            _ if name.starts_with("test_") => ItemType::Test,
            _ if name.contains("_impl") => ItemType::Implementation,
            _ => ItemType::Regular,
        }
    }
    
    // Main function uses the pure classifier
    fn process(&mut self, name: &str) {
        let item_type = Self::classify_item(name);
        // ... rest of logic
    }
}
```
