skills/cleanup-codebase/SKILL.md
Reduce concepts, duplication, and ceremony in internal code while touching nearby code. Use when working an existing path and you spot dead fields, redundant wrappers, or speculative abstractions; distinct from refactor-break-bw-compat (internal hygiene, not public API removal).
npx skillsauth add outlinedriven/odin-codex-plugin cleanup-codebaseInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Code rots in two directions: outward (drift from the original design) and downward (accretion of dead state, redundant indirection, speculative ceremony). This skill addresses the second. The thesis is local: you are already in nearby code for some other reason; while you are there, remove what does not earn its keep.
Modern insight (2025): Kent Beck's Tidy First? thesis and Casey Muratori's dataflow-first design heuristic both converge on the same conclusion that this skill's compress-side operations operationalize — small, frequent, atomic cleanups embedded in the active commit stream beat scheduled "cleanup PRs" by a wide margin. Scheduled cleanups bundle unrelated concerns and become unreviewable; embedded cleanups stay reviewable because their scope is the file already in your hands.
See dead-fields for examples of dead struct fields, props, and class members. See redundant-wrappers for examples of single-line passthrough functions that should be inlined. See dead-config for stale feature flags, environment variables, and dead config branches.
These are mandates, not suggestions. Internalize them as rules; do not paraphrase.
Every concept the reader has to hold in their head has a cost. Every duplicated piece of logic has two places to drift apart. Every ceremonial wrapper, factory, or builder that does not protect a real boundary is a tax on every future reader. Reducing concepts is not the same as reducing lines — it is reducing the number of distinct things a reader has to track.
A "contract" is the truth about what some piece of state means or what some operation does. It must have exactly one owner. Mirroring (two structures holding the same field; two services maintaining the same cached state) is a guaranteed-future-bug pattern. Wrappers are coupling-removal tools — if a wrapper just renames or forwards without removing coupling, it is ceremony.
"Speculative" means "I might need this later." You will not need it later in the form you imagine now, and the indirection you add now will make the actual future change harder. Indirection earns its keep when it removes coupling that currently exists, or when it protects a current boundary (process, untrusted-input, async/sync seam). Otherwise it is dead-weight ceremony.
A "local change" that has to ripple through three wrapper layers is not local — it is coupled. If caller and callee are both yours, both in the same module, with no API boundary between them, refactoring them in lockstep is correct. Resist the urge to "preserve the interface" of internal functions; an internal function's interface is whoever calls it.
Dead code is not free. It misleads readers about what the system does, it survives grep searches and pulls attention, it tricks reviewers into preserving it "just in case." While you are in nearby code for some other reason, remove what is dead. Make the deletion its own atomic commit so reviewers see exactly what disappeared.
Indirection earns its keep at: public API surfaces, process/network seams (RPC, HTTP, queues), untrusted-input boundaries, async/sync seams, runtime seams (FFI, WASM, JNI), and test/production seams where mocks legitimately substitute. A swappable-implementation contract counts only when >1 real impl ships today — not "might exist later."
Not boundaries: internal modules in the same crate/package, helpers in the same file, cross-module calls without a constraint that prevents co-change.
foo.py for an unrelated feature; while reading the file, you notice a dead field<git> charter's "one concern per commit" rule. Solution: git move --fixup to embed the cleanup as an atomic commit alongside the active change.refactor-break-bw-compat's territory and needs a migration plan.| Pattern | Action | Notes |
|---------|--------|-------|
| Wrapper that adds nothing but a rename | Inline, then delete the wrapper | Renames are not abstractions |
| Field set in constructor, never read after | Delete the field and its assignment | Grep all consumers first |
| Config flag where both branches are dead (always-on or always-off) | Delete the flag, keep the winning path | Often legacy migration debt |
| Adapter between two structurally equivalent local types | Collapse to one type | Different names ≠ different concepts |
| Helper used in 3+ places that genuinely names a shared concept | Keep | Real reuse, real naming |
| Helper used in 1 place that wraps a 2-line body | Inline | The wrapper is overhead |
| State mirrored across two services / two structs | Pick one owner; the other reads from it | Mirroring is the bug |
| Comment that contradicts the code | Update or delete the comment | Stale comments mislead |
| TODO from > 6 months ago | Open issue or delete | Indefinite TODOs are noise |
git --no-pager grep -n (or ast-grep) to verify no consumers; check tests, docs, configs, error messages.~/.claude/claude/system-prompt-baseline.md <git> charter — cleanup is its own atomic commit. If it is mixed in with behavior change, split via git move --fixup / git split.git rip the file or precise Edit for partial removal; never comment-out.tests-purge-unneeded.git move --fixup so each commit has exactly one concern. Cleanup commits ride alongside behavior commits in the same PR; that is fine and encouraged.| Gate | Pass Criteria | Blocking |
|------|---------------|----------|
| Atomic commit | Cleanup is its own commit, separate from behavior change | Yes |
| Dead confirmation | grep/ast-grep confirms no consumers in code, tests, docs, configs | Yes |
| No new abstractions | Diff is net-deletion (or inline-and-delete) only | Yes |
| Build + tests pass | Repo-native verification on every touched language | Yes |
| Ghost search | No leftover references in docs, error messages, env vars | Yes |
| Code | Meaning |
|------|---------|
| 0 | Clean — atomic deletion landed, all consumers updated, build green |
| 11 | Consumer found that was not in the original grep — investigate and either preserve or migrate |
| 12 | Build / test regression — rollback required |
| 13 | Mixed-concern commit — must split via git move --fixup before merging |
| 14 | New abstraction introduced — separate the commit, justify the abstraction independently |
| 15 | Ghost references found — cleanup incomplete |
testing
ODIN's compress-operations dispatcher under the Compressor/Extender role. Invoke on "tidy", "clean up", "tidy this file/memory/workspace/git/docs", or when active context (current file, diff, stack, memory directory) has structural rot to resolve before touching behavior. Detects target domain from context and routes to the sibling skill. Requires explicit target or clear active-context signal — do not invoke speculatively.
development
Cross-domain taste skill — apply distinctive judgment to any artifact (prose, code, design, decisions) instead of converging to AI defaults. Two modes — `audit` (judge work against the two-sided charter and portable anchors) and `anchor` (load register before producing). Auto-detects by phrasing; override via `/taste audit | anchor`. Trigger on "is this slop?", "overkill?", "elegant?", "taste-test this".
tools
One-shot bootstrap of strict-mode tooling per ecosystem plus per-task GOALS.md scaffolding so an agentic loop can self-verify. Writes typechecker/linter/schema-validator config for TS (strict + noUncheckedIndexedAccess + exactOptionalPropertyTypes), Python (Pyright strict, Ruff strict), Rust (Clippy deny-correctness), Go (golangci-lint with staticcheck), OCaml (dune --release); establishes `.agent-tasks/<id>/GOALS.md` per-task convention distinct from project-stable AGENTS.md. C++/Java/Kotlin and framework specifics (Spring Boot, Nest, React-strict) are out of scope. Trigger on new project bootstrap, agentic-task setup, "make this self-verifying", "set the loop's goal", "scaffold goals for this issue". Pairs with `llm-self-loop` runtime.
tools
Install git pre-commit hooks via the project's hook tool — Husky+lint-staged (JS), pre-commit (Python/OCaml), lefthook (Go), cargo-husky (Rust). Use when the user wants commit-time formatting, linting, type-checking, or test gates. Detects ecosystem first.