name: debt-sweep
description: "Autonomous themed tech-debt cleanup. Reads docs/architecture/debt-ledger.yml, rotates to the least-recently-served debt theme, works it for a time budget (default 2h) or until drained, opens one PR, and asks judgment questions inline at the end. Use for daily debt burndown without Peter pointing at a target: grandfathered analyzer rules, architecture-test baselines, obsolete-field reads, cross-section stitching."
argument-hint: "[--budget=2h] [--theme=] [--inventory]"
Debt Sweep
See docs/superpowers/specs/2026-06-12-debt-sweep-design.md for full design.
Invocation
| Flag | Behavior |
|---|---|
| (none) | rotation picks the theme; ~2h budget |
--budget=<duration> |
override budget (30m, 4h). Wall-clock guidance, checked between items, never mid-item |
--theme=<id> |
skip rotation, work this theme |
--inventory |
full re-scan: re-run every theme's detect, merge new classes, refresh counts, evict gone entries. Combinable with a normal sweep or standalone (standalone still PRs the ledger update) |
Phase 0: Setup
REPO_ROOT=$(git rev-parse --show-toplevel). Parse --budget (default 2h); record start time.
Phase 1: Worktree
git fetch origin main
TS=$(date -u +%Y-%m-%dT%H%M%SZ)
git worktree add $REPO_ROOT/.worktrees/debt-sweep-$TS -b debt-sweep/$TS origin/main
WORKTREE=$REPO_ROOT/.worktrees/debt-sweep-$TS # cd here; all commands run inside
Scope is frozen here. Never re-fetch, re-resolve, or reconcile against origin/main mid-run — a parallel session merging a PR while the sweep runs is expected and irrelevant. Anything landing after the branch point is the next sweep's input. Path/branch collision → error; instruct git worktree list / git worktree remove.
Scope every Glob/Grep to $WORKTREE paths — never the repo root (it holds other worktrees).
Phase 2: Ledger + staleness check
- Read
docs/architecture/debt-ledger.yml. Validate:version: 1; every theme hasid,title,detect,review(light|panel; theinboxtheme alone usesper-item— each inbox entry carries its own tier),last_swept,remaining. Parse error → abort (no partial run), report to Peter. - Staleness check (every run, cheap):
- Distinct
HUM####ids in real[Grandfathered(attribute usages vs. ledger theme ids → each missing rule becomes a new theme (last_swept: never,review: lightunless it is plainly a structural/judgment rule — thenpanel). Anchor the grep to attribute syntax — an unanchored[Grandfathered(also matches analyzer doc-comments and message strings, which seeds phantom themes:grep -rn -A3 --include='*.cs' '^[[:space:]]*\[Grandfathered(' $WORKTREE/src | grep -oE '"HUM[0-9]{4}"' | sort -u - Files in
tests/Humans.Application.Tests/Architecture/Baselines/with >0 non-comment entries vs. ledger → same. - New themes are committed in the ledger update (Phase 5) even when not worked this run.
- Distinct
--inventoryonly: re-run every theme'sdetect, refresh allremaining, evict themes whose debt is gone (note each eviction in the report), and look for debt classes the ledger misses (new[Obsolete]clusters, new custom warning ids in a fresh build, new ratchet analyzers).
Build-derived counts
Warning-backed themes (detect: build:<CODE>) are counted from one build log instead of per-theme commands:
dotnet build Humans.slnx -v quiet --no-incremental 2>&1 | tee /tmp/debt-sweep-build.log >/dev/null
grep -E "warning <CODE>" /tmp/debt-sweep-build.log | sed 's/ \[.*//' | sort -u | wc -l # distinct sites
Run this build once in Phase 3 (it doubles as the baseline build) and reuse the log for every build: detect this run.
Phase 3: Pick theme
--themeif given; else: order themes bylast_sweptascending (neverfirst), skipremaining: 0.- Run the candidate's
detectto confirmremaining > 0. If 0 → apply the drain rule (below), update the entry, take the next candidate. - Enumerate the theme's concrete items (file list from grep, baseline lines, distinct warning sites). Order items so anything in a
recent_sectionssection is worked last. - Fold in
inboxitems that match the chosen theme.
Drain rule: when a theme hits 0 and a structural enforcer guards regression (analyzer at Error with no remaining grandfathers; architecture test whose baseline is empty), retire the entry — delete it from the ledger and note it in the report. The Phase 2 staleness check re-creates it if the debt ever reappears. Themes without an enforcer stay listed at remaining: 0 and are only re-checked by --inventory.
Phase 4: Work loop
Until budget exhausted or theme drained, per item (one item or one tight cluster per commit):
Budget checks are real clock reads, never estimates. Between items run date -u +%H:%M and compare against the Phase 0 start time. Narrative time-feel drifts badly (first run stopped at 27 real minutes while "estimating" 85); the only valid stop-on-budget is one backed by a date call in the transcript.
- Fix it right — no surgical fixes (constitution). Reuse-first; match existing patterns (the theme's
notesoften names the reference implementation). dotnet build Humans.slnx -v quiet.- Targeted tests: the touched section's tests +
--filterthe Architecture tests. Fulldotnet test Humans.slnx -v quietat minimum before each push. - EF model-drift gate (themes touching entities, navs, or
Data/Configurations): the sweep never creates migrations, so verify the fix didn't silently change the model —cd src/Humans.Infrastructure && dotnet ef migrations has-pending-model-changes; pending changes → revert the item, classify it as schema work, record it (Phase 7 question or inbox). - Forbidden-move grep on the item diff:
#pragma warning disable HUM,[SuppressMessage, new[Grandfathered],// ReSharper disable, visibility narrowing that dodges a rule rather than fixes it. Any hit → revert the item, record in report. Also forbidden (judgment, not grep): splitting a controller method into controller-local helpers (private methods, static helpers, local functions, VM factories) to satisfy a per-method metric. The metric is a proxy for "no business logic in controllers" — the fix is a service move, or an honest "this is presentation; the threshold is miscalibrated" finding (grandfather stays, record it). Rejected and reverted wholesale on 2026-06-12. review: panelthemes only: dispatch a second-opinion reviewer subagent — opus-tier, read-only, score-blind, default-reject (refactor-swarm posture): "is this fix a good idea, not merely green — name the concept that improved in one sentence." Name itdebt-review-<item>-opus, description tagged(opus). Reject → rework once; persistent reject → revert, skip item, record.- Commit with a one-line
debt(<theme-id>): <what>message. Push every 3–5 items.
Rules of the loop:
- Stop-and-ask classes are skip-and-ask classes here: interface/public-surface additions (
interface-method-additions-are-debt), DB schema work of any kind, privilege changes → skip the item, queue a Phase 7 question. Never block the loop waiting. - Off-theme debt discovered while working → append to ledger
inbox, never chased. - Mechanical edit fan-out is allowed via edit-only subagent workers (sonnet, named
<task>-sonnet, absolute$WORKTREEpaths, no git/build); the orchestrator owns all git and build commands. - An item that can't be made green after a genuine attempt → revert it cleanly, record, continue. Never leave the branch red between commits.
Phase 5: Ledger update + report
In the worktree:
- Theme entry:
last_swept: <today>,remaining:from re-runningdetect, apply drain/retire rule. recent_sections:← dominant sections of the last ~3 sweeps (this one included).- Add Phase 2 staleness discoveries; append work-loop inbox items; remove inbox items completed this run; apply
--inventoryevictions. - Overwrite
docs/debt/last-report.md: timestamp, theme, budget used; items fixed (one line each: what + commit sha); items skipped + why (schema, interface-addition, panel-reject, budget); forbidden-move reverts; inbox additions; ledger changes (new themes, retirements, evictions).
Commit ledger + report with the work (same PR).
Phase 6: PR
git push -u origin debt-sweep/$TS
gh pr create --repo peterdrier/Humans --base main \
--title "debt: <theme title> — N items" \
--body "$(cat <<'EOF'
## Theme
<theme id — one-line description>
## Fixed
<per-item bullets>
## Skipped
<item — reason>
## Ledger
<delta: counts, new themes, retirements>
Report: `docs/debt/last-report.md` (committed in this PR).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
EOF
)"
One PR per sweep. Print the PR URL.
Phase 7: Resolve review items inline (no homework)
After the PR is open and before teardown, present every item that needs Peter's judgment inline in chat — terse, numbered, one line each, answerable in a word or two: skipped schema/interface items, panel rejects worth debating, uncertain classifications, anything queued during the loop. The report is the record, not the delivery channel — assume Peter never opens it. Inline prose, never the AskUserQuestion tool (project rule).
Wait for answers. Apply resulting edits in the worktree, git add + new git commit (a bare push after editing sends nothing), update the report's skipped/questions sections with each resolution, git push.
Zero judgment calls → say so in one line and proceed.
Phase 8: Teardown
Only after Phase 7 resolves: cd $REPO_ROOT && git worktree remove $WORKTREE (--force only if Phase 6 errored). Never rm -rf. Branch stays on origin until the PR closes.
Adding debt to the ledger (any session, any time)
- Recurring class → append a
themes:entry withlast_swept: never(rotation serves it next automatically). Pickreview:honestly:lightonly when the fix is rule-prescribed and the verifier is mechanical. - One-off item → append to
inbox:withadded: <date>and a one-linewhat:. - Ledger-only changes ride the discovery PR or go direct to
origin/mainperno-direct-to-main.
Standing constraints
- No EF migrations, ever. No schema changes, no snapshot edits, no
dotnet ef migrations add. DTO/view-model/non-entity property drops are fine. The Phase 4 drift gate enforces this. - Never touch
[DontFix]— Peter-applied permanent exceptions; skip those sites entirely. - No analyzer suppressions in any form (
no-analyzer-suppressions). - No data migrations / backfills (
no-data-backfills). - Explicit subagent models, tagged in name + description (sonnet workers, opus-tier panel reviewers).
- The sweep touches only: the theme's item files,
docs/architecture/debt-ledger.yml, anddocs/debt/last-report.md. - After the run, update
docs/architecture/maintenance-log.mdpermaintenance-log-update(separate from the sweep PR if needed).
Failure modes
| Failure | Behavior |
|---|---|
| Ledger YAML parse error | Abort before any work; report |
detect command fails |
Skip theme, pick next; record |
| Item breaks build/tests, can't right it | Revert item, record, continue |
| Panel rejects twice | Revert, skip, record |
| EF drift gate fires | Revert item, classify as schema work, record |
| Budget hit mid-theme | Normal: commit what's done; ledger carries the remainder |
| Push / PR fails | Worktree retained; fix manually |