# Entropy Management

Entropy management is the third pillar of harness engineering. It is the practice of actively fighting the natural decay that accumulates in any codebase over time -- decay that is amplified by the velocity of AI-generated code.

Without entropy management, a codebase that starts clean will gradually degrade: documentation drifts from reality, dead code accumulates, dependencies go stale, patterns fragment, and the harness itself becomes unreliable. Entropy management is the discipline that prevents this.

> "Technical debt is a high-interest loan. You can take it out, but if you do not pay it back systematically, the interest will crush you."

---

## Why AI-Generated Code Amplifies Entropy

Traditional codebases accumulate entropy at the speed of human typing. AI-generated codebases accumulate entropy at the speed of model inference. Three dynamics drive this:

### 1. Volume
An agent can produce 500 lines in minutes. A team running multiple agents can produce thousands of lines per day. Each line is a potential source of entropy: an unused import, a slightly different naming convention, a dependency that is almost but not quite the same as the one used elsewhere.

### 2. Session Isolation
Each agent session starts fresh. The agent that wrote the payments module last Tuesday has no memory of doing so. The agent writing the inventory module today may introduce subtly incompatible patterns. Without active entropy management, pattern drift is inevitable.

### 3. Speed of Change
When code changes rapidly, documentation falls behind faster. A function that was documented yesterday may have been refactored twice since then. Tests written for one version may not cover the current version. The faster you move, the more entropy you generate.

---

## The Garbage Collection Pattern

Garbage collection in software runtimes reclaims memory that is no longer referenced. In harness engineering, "garbage collection" reclaims codebase health by cleaning up what no longer serves a purpose.

### What to Collect

| Type of Garbage | How to Detect | Impact If Ignored |
|----------------|--------------|-------------------|
| Dead code | No callers, no tests, no references | Confuses agents, inflates codebase |
| Unused imports | Static analysis | Slower builds, cluttered files |
| Stale documentation | Doc references code that no longer exists | Agents follow outdated instructions |
| Orphaned tests | Tests for deleted functions | False confidence in test coverage |
| Unused dependencies | Not imported anywhere | Security risk, slower installs |
| Deprecated patterns | Old patterns coexisting with new ones | Agents copy the wrong pattern |
| Commented-out code | Comments containing code syntax | Agents may uncomment and use it |

### Implementing a GC Agent

A garbage collection agent is a scheduled task that scans the codebase for specific types of entropy and either fixes them or reports them.

```markdown
# .claude/gc-agent-prompt.md

## Task: Codebase Garbage Collection

Scan the repository and fix the following:

1. **Dead code**: Find functions, classes, and constants with zero callers.
   Remove them. If unsure, add a TODO comment instead of removing.

2. **Unused imports**: Find and remove imports that are not used in the file.

3. **Stale docs**: Compare docs/ files against actual code. Flag any doc that
   references a file, function, or pattern that no longer exists.

4. **Orphaned tests**: Find test files whose corresponding source file
   has been deleted. Remove the test file.

5. **Commented-out code**: Find blocks of commented-out code longer than 3 lines.
   Remove them (git history preserves the original).

After making changes:
- Run `make verify` to ensure nothing broke.
- Create a commit with message: "chore: garbage collection [automated]"
- List everything that was changed and why in the PR description.
```

### Scheduling

Run garbage collection on a cadence that matches your development velocity:

- **Small team (1-3 agents)**: Weekly
- **Medium team (3-10 agents)**: Twice per week
- **Large team (10+ agents)**: Daily

---

## Doc-Gardening Agents

Documentation rot is the most insidious form of entropy because it corrupts the context engineering pillar. When docs are wrong, agents produce wrong code confidently.

### What Doc-Gardening Agents Do

1. **Cross-reference docs with code**: Check that every file, function, or pattern mentioned in docs actually exists.
2. **Update examples**: Ensure code examples in docs compile and pass tests.
3. **Verify links**: Check that all internal links between docs point to files that exist.
4. **Flag contradictions**: Find docs that give conflicting instructions.
5. **Check freshness**: Identify docs that have not been updated since the code they describe was significantly changed.

### Implementation

```powershell
# scripts/Invoke-DocGardener.ps1
# Verify that documentation references match reality.
# Run: pwsh ./scripts/Invoke-DocGardener.ps1

[CmdletBinding()]
param(
    [string]$DocsRoot = 'docs',
    [string]$RepoRoot = (Get-Location)
)

$issues = [System.Collections.Generic.List[string]]::new()

function Test-FileReference {
    param([string]$DocPath, [string]$Content)

    # Match patterns like `src/Project.Web/Pages/Index.cshtml.cs`
    $regex = '`((?:src|tests|samples)/[^`]+\.(cs|fs|cshtml|razor|csproj|fsproj|json|ps1|psm1|md))`'
    foreach ($m in [regex]::Matches($Content, $regex)) {
        $ref = $m.Groups[1].Value
        if (-not (Test-Path -LiteralPath (Join-Path $RepoRoot $ref))) {
            $script:issues.Add("$DocPath references '$ref' which does not exist. Move or delete?")
        }
    }
}

function Test-CommandReference {
    param([string]$DocPath, [string]$Content)

    # `dotnet test`, `dotnet build`, etc. -- verify the projects they hit exist.
    foreach ($m in [regex]::Matches($Content, '`dotnet test ([^\s`]+)`')) {
        $project = $m.Groups[1].Value
        if (-not (Test-Path -LiteralPath (Join-Path $RepoRoot $project))) {
            $script:issues.Add("$DocPath references 'dotnet test $project' but that path does not exist.")
        }
    }
}

function Test-InternalLink {
    param([string]$DocPath, [string]$Content)

    $docDir = Split-Path -Parent $DocPath
    foreach ($m in [regex]::Matches($Content, '\[[^\]]*\]\(((?!https?:)[^)#]+)(#[^)]*)?\)')) {
        $target = Join-Path $docDir $m.Groups[1].Value
        if (-not (Test-Path -LiteralPath $target)) {
            $script:issues.Add("$DocPath has broken link to '$($m.Groups[1].Value)'.")
        }
    }
}

Get-ChildItem -Path $DocsRoot -Recurse -Filter *.md | ForEach-Object {
    $content = Get-Content -Raw -LiteralPath $_.FullName
    Test-FileReference   -DocPath $_.FullName -Content $content
    Test-CommandReference -DocPath $_.FullName -Content $content
    Test-InternalLink    -DocPath $_.FullName -Content $content
}

if ($issues.Count -gt 0) {
    Write-Host "Found $($issues.Count) documentation issues:`n" -ForegroundColor Red
    $issues | ForEach-Object { Write-Host "  - $_" }
    exit 1
}
Write-Host 'All documentation references are valid.' -ForegroundColor Green
```

---

## Quality Grades Tracking

Quality grades provide a longitudinal view of codebase health. Instead of binary pass/fail, they track trends over time.

### The Grading System

Assign each module a quality grade based on measurable criteria:

| Grade | Criteria |
|-------|---------|
| **A** | Full test coverage, typed, documented, passes all structural tests, no TODOs |
| **B** | Good test coverage (>80%), typed, documented, passes structural tests |
| **C** | Partial test coverage (50-80%), some types, basic docs |
| **D** | Low test coverage (<50%), few types, outdated or missing docs |
| **F** | No tests, no types, no docs, structural violations |

### Tracking Grades Over Time

```powershell
# scripts/Get-QualityGrades.ps1
# Calculate quality grades for each project in the solution.
# Outputs a report and writes a dated row into docs/quality-grades.csv.

[CmdletBinding()]
param(
    [string]$SrcRoot   = 'src',
    [string]$TestsRoot = 'tests',
    [string]$History   = 'docs/quality-grades.csv'
)

function Get-CoverageRatio {
    param([string]$ProjectName)
    $testProj = Join-Path $TestsRoot "$ProjectName.UnitTests"
    if (-not (Test-Path $testProj)) { return 0.0 }
    $src   = (Get-ChildItem -Path (Join-Path $SrcRoot $ProjectName) -Recurse -Filter *.cs).Count
    $tests = (Get-ChildItem -Path $testProj -Recurse -Filter *.cs).Count
    if ($src -eq 0) { return 0.0 }
    return [math]::Round($tests / [double]$src, 2)
}

function Test-NullableEnabled {
    param([string]$ProjectName)
    $csproj = Get-ChildItem -Path (Join-Path $SrcRoot $ProjectName) -Recurse -Filter *.csproj | Select-Object -First 1
    if (-not $csproj) { return $false }
    return (Get-Content $csproj.FullName -Raw) -match '<Nullable>\s*enable\s*</Nullable>'
}

function Get-Grade {
    param([double]$Coverage, [bool]$Nullable, [bool]$HasReadme, [int]$TodoCount)
    if ($Coverage -ge 0.95 -and $Nullable -and $HasReadme -and $TodoCount -eq 0) { return 'A' }
    if ($Coverage -ge 0.80 -and $Nullable -and $HasReadme)                       { return 'B' }
    if ($Coverage -ge 0.50)                                                       { return 'C' }
    if ($Coverage -ge 0.20)                                                       { return 'D' }
    return 'F'
}

$rows = Get-ChildItem -Path $SrcRoot -Directory | ForEach-Object {
    $name      = $_.Name
    $coverage  = Get-CoverageRatio -ProjectName $name
    $nullable  = Test-NullableEnabled -ProjectName $name
    $hasReadme = Test-Path (Join-Path $_.FullName 'README.md')
    $todos     = (Select-String -Path (Join-Path $_.FullName '*.cs') -Pattern 'TODO|HACK|FIXME' -Recurse -ErrorAction SilentlyContinue).Count
    [pscustomobject]@{
        Date     = (Get-Date -Format 'yyyy-MM-dd')
        Module   = $name
        Grade    = Get-Grade $coverage $nullable $hasReadme $todos
        Coverage = $coverage
        Nullable = $nullable
        HasDocs  = $hasReadme
        Todos    = $todos
    }
}

$rows | Format-Table -AutoSize
$rows | Export-Csv -Path $History -Append -NoTypeInformation
```

def save_report(grades: list[dict]):
    report_dir = Path("reports/quality")
    report_dir.mkdir(parents=True, exist_ok=True)

    report_path = report_dir / f"{date.today().isoformat()}.json"
    report_path.write_text(json.dumps(grades, indent=2))

    print(f"Quality report saved to {report_path}")
    for g in grades:
        print(f"  {g['grade']} | {g['module']} (coverage: {g['test_coverage']}, TODOs: {g['todo_count']})")
```

### Using Grades for Prioritization

Quality grades tell you where to focus entropy management efforts:

- **F modules**: Immediate attention. These are the biggest risk. Assign an agent to add basic tests and types.
- **D modules**: Schedule improvement. Add a task to each sprint to bring one D module to C.
- **C modules**: Opportunistic improvement. When an agent modifies a C module, it should also improve coverage.
- **A/B modules**: Maintain. Ensure no regression. These are your reference implementations that agents should emulate.

---

## Technical Debt as High-Interest Loan

Frame technical debt as a financial instrument to communicate its impact:

### The Interest Rate

Every piece of technical debt has an interest rate -- the ongoing cost of not fixing it:

- **Dead code**: Low interest. Confusing but not breaking. Fix during garbage collection.
- **Missing tests**: Medium interest. Bugs hide longer, agent output is unverified. Fix proactively.
- **Stale documentation**: High interest. Agents produce wrong code. Every session pays interest. Fix immediately.
- **Architectural violations**: Very high interest. Pattern drift accelerates, boundaries erode. Fix before it spreads.

### The Debt Register

Maintain a machine-readable debt register that agents can consult:

```yaml
# docs/tech-debt.yml
debts:
  - id: TD-001
    title: "Legacy payment processor integration"
    location: "src/services/payments/legacy_processor.py"
    interest: high
    description: "Uses deprecated API. Will stop working when v1 is sunset."
    remediation: "Migrate to v2 API. See docs/decisions/005-payment-api-v2.md"
    created: 2025-01-15
    deadline: 2025-06-01

  - id: TD-002
    title: "Missing input validation on /orders endpoint"
    location: "src/api/orders.py"
    interest: medium
    description: "No request body validation. Malformed input causes 500 errors."
    remediation: "Add Pydantic model for OrderCreateRequest. See src/api/users.py for pattern."
    created: 2025-02-01
```

Agents can read this register and factor known debt into their implementation decisions.

---

## The "Ralph Wiggum Loop" (Self-Review Pattern)

Named for the Simpsons character who grades his own tests, this pattern has agents review their own output before submitting it. Despite the humorous name, it is surprisingly effective.

### How It Works

```
1. Agent generates code (first pass)
2. Agent reviews its own code against project standards (self-review)
3. Agent fixes issues found in self-review (second pass)
4. Automated verification runs (tests, linting, structural checks)
5. Human reviews the verified output (final review)
```

### Implementation

Include self-review instructions in your agent configuration:

```markdown
# In .github/copilot-instructions.md or task prompts

## Before Submitting
After writing your code, review it against these criteria:

1. **Naming**: Are all names descriptive and consistent with existing conventions?
2. **Error handling**: Does every error path have appropriate handling?
3. **Tests**: Did you write tests for happy path AND error cases?
4. **Types**: Are all function signatures fully typed?
5. **Imports**: Are you importing from the correct layer? (See docs/architecture.md)
6. **Duplication**: Does this duplicate logic that already exists elsewhere?

If you find issues, fix them before running verification.
```

### Why Self-Review Works

Models are better at *evaluating* code than *generating* it. The first pass optimizes for completing the task. The self-review pass optimizes for quality. These are different objectives, and separating them produces better results than trying to optimize both simultaneously.

---

## Automated Cleanup Processes

Beyond periodic garbage collection, certain cleanup processes should run continuously:

### Dependency Updates

```yaml
# Dependabot or Renovate configuration
# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: "nuget"
    directory: "/"
    schedule:
      interval: "weekly"
    groups:
      microsoft-dependencies:
        patterns: ["Microsoft.*"]
      azure-dependencies:
        patterns: ["Azure.*"]
      other-dependencies:
        patterns: ["*"]
    commit-message:
      prefix: "deps"
```

### Format on Save / Format on Commit

Formatting should never be a manual step or a review comment:

```yaml
# .pre-commit-config.yaml
repos:
  - repo: local
    hooks:
      - id: dotnet-format
        name: dotnet format
        entry: dotnet format --verify-no-changes --severity error
        language: system
        pass_filenames: false
      - id: psscriptanalyzer
        name: PSScriptAnalyzer
        entry: pwsh -NoProfile -Command "Invoke-ScriptAnalyzer -Path . -Recurse -EnableExit"
        language: system
        pass_filenames: false
```

### Stale Branch Cleanup

```yaml
# .github/workflows/cleanup-branches.yml
name: Cleanup Stale Branches
on:
  schedule:
    - cron: "0 0 * * 0"  # Weekly on Sunday

jobs:
  cleanup:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Delete merged branches older than 7 days
        run: |
          git branch -r --merged origin/main |
            grep -v main |
            sed 's/origin\///' |
            xargs -I{} git push origin --delete {}
```

---

## Scheduled Maintenance Agents

Schedule agents to perform specific maintenance tasks at regular intervals:

### Weekly: Documentation Audit

```markdown
## Weekly Doc Audit Task

Review all files in docs/ and verify:
1. Every code reference points to a file that exists
2. Every command reference works (run it and check)
3. No doc contradicts another doc
4. All docs have been updated within the last 30 days or are flagged for review

Output: a list of issues found and fixes applied.
```

### Bi-Weekly: Pattern Consistency Check

```markdown
## Pattern Consistency Task

Scan the codebase and verify:
1. All services follow the same structure (constructor, methods, error handling)
2. All API handlers follow the same pattern (validation, service call, response)
3. All test files follow the same structure (setup, test cases, teardown)
4. No module uses a deprecated pattern when a newer one exists

Output: a list of inconsistencies and suggested fixes.
```

### Monthly: Comprehensive Quality Review

```markdown
## Monthly Quality Review Task

Generate a quality report:
1. Run quality grades for all modules
2. Compare against last month's report
3. Identify modules whose grade dropped
4. Identify the top 3 improvement opportunities
5. Create issues for the top 3 improvements

Output: quality report with trends and action items.
```

---

## Continuous Quality Monitoring

Set up dashboards or reports that track codebase health over time:

### Metrics to Track

| Metric | What It Measures | Target |
|--------|-----------------|--------|
| Test coverage | Percentage of code exercised by tests | >80%, trending up |
| Lint violations | Number of unresolved lint issues | 0, always |
| Structural test pass rate | Percentage of architectural constraints passing | 100%, always |
| TODO count | Number of TODO/FIXME/HACK comments | Trending down |
| Dead code volume | Lines of unreferenced code | Trending down |
| Doc freshness | Average age of docs since last update | <30 days |
| Dependency age | Average age of dependencies since last update | <90 days |
| Agent first-pass success rate | Percentage of agent runs that pass verification on first try | Trending up |

### Alerting

Set thresholds that trigger action:

- **Test coverage drops below 75%**: Block merges until coverage is restored.
- **Structural test failures**: Block merges (non-negotiable).
- **TODO count exceeds 50**: Schedule a cleanup sprint.
- **Doc freshness exceeds 60 days**: Trigger doc-gardening agent.
- **Agent first-pass rate drops below 60%**: Review recent harness changes for regressions.

---

## Event-Driven Maintenance

Time-based maintenance catches slow drift. But harness docs also need **event-driven updates** when significant changes land. These two modes complement each other:

- **Event-driven:** Triggered by feature merges, architecture changes, dependency additions. See [Maintaining Your Harness](maintaining-your-harness.md) for the full guide and [the post-implementation checklist](../checklists/post-implementation.md).
- **Time-driven:** Scheduled weekly, monthly, quarterly sweeps covered in this document.

The principle: update at merge time (cheap), don't catch up later (expensive). Harness doc updates should be part of the same PR as the feature that triggers them.

Event-driven maintenance covers acute changes -- a new module, a database migration, a major dependency addition. Time-driven maintenance covers chronic drift -- renamed variables, moved files, gradually evolved patterns. Together, they keep the harness accurate.

For more detail on event-driven workflows, the change-impact matrix, and CI enforcement of doc updates, see [Maintaining Your Harness](maintaining-your-harness.md).

---

## Getting Started with Entropy Management

If you have done nothing for entropy management, start here:

### Week 1: Set Up Quality Grades
Run a quality grade assessment of your codebase. Establish your baseline. Identify your F-grade modules.

### Week 2: Schedule Garbage Collection
Set up a weekly garbage collection agent run. Start with unused imports and dead code -- they are the easiest to detect and lowest risk to remove.

### Week 3: Add Doc Gardening
Run the doc gardener script. Fix broken references. Commit to keeping docs in sync going forward.

### Week 4: Track Metrics
Set up a simple dashboard (even a JSON file committed to the repo) that tracks quality grades over time. Review it in your team standup.

### Ongoing
Add one new entropy management process per month. Within a quarter, you will have a self-maintaining codebase that stays healthy without constant human vigilance.

---

## Further Reading

- [What Is Harness Engineering?](what-is-harness-copilot.md) -- How entropy management fits into the three pillars.
- [Context Engineering](context-engineering.md) -- Entropy in docs degrades context quality.
- [Architectural Constraints](architectural-constraints.md) -- Constraints prevent new entropy; entropy management cleans up old entropy.
- [Anti-Patterns](anti-patterns.md) -- Ignoring entropy is a key anti-pattern.
