| name | codebase-hygiene |
| description | Two-mode skill: (1) find semantic duplicates — functions doing the same thing
under different names, invisible to copy-paste detectors; (2) deepen shallow
modules — thin wrappers and pass-through layers that spread complexity.
Advisory and read-only; changes route through BUILD with full gates.
|
| allowed-tools | Read Grep Glob Bash |
| user-invocable | false |
Codebase Hygiene (Duplicate Detection + Module Deepening)
Advisory and read-heavy. Diagnoses and proposes; does not refactor. Any actual change goes through BUILD with full gates.
Mode: DUPLICATE DETECTION
Semantic duplicates: functions serving the same purpose but implemented independently under different names. Copy-paste detectors catch syntactic duplicates; this finds "same intent, different implementation."
Method
- Extract catalog — Grep/Glob for exported functions. Record
name | file:line | signature.
- Categorize by domain (cheap tier) — validation, formatting, path manipulation, HTTP shaping, date handling. Mechanical bucketing to shrink comparison space.
- Drop categories with <3 functions — can't hide a meaningful duplication pattern.
- Detect duplicates per category (capable tier) — read implementations, decide which share intent. Never use cheap tier for detection — it anchors on names and rubber-stamps "these look different."
- Emit findings — group by confidence, highest first. Each finding: what is duplicated, why it matters, the fix,
file:line evidence. Route through code-reviewer finding contract.
High-Risk Zones
| Zone | Why it duplicates |
|---|
utils/, helpers/, lib/ | Catch-all dumping grounds |
| Validation code | "Is this a valid email/id/url" rewritten per feature |
| Error formatting | Every module invents its own Error → string |
| Path manipulation | Join/normalize/relativize reimplemented |
| String formatting | Truncate, slugify, titlecase, pad re-rolled |
| Date formatting | Parse/format/diff scattered |
| API response shaping | Envelope/pagination/error-body copied per endpoint |
Consolidation Discipline
Never delete a duplicate until all three hold:
- Survivor has tests — pick the implementation with real coverage. If neither has tests, write the test against the chosen survivor first.
- All callers updated — Grep to enumerate callers. Missing one is a silent break.
- Re-run after consolidation — test suite + build/typecheck pass. Green tests on the survivor license the deletion.
Mode: MODULE DEEPENING
Existing code works; the problem is shape, not features. LLM-grown codebases accrete shallow modules — thin wrappers, pass-through layers, near-duplicate helpers. Each looks harmless; together they spread complexity across every caller.
Deep module: hides a lot of behavior behind a small interface.
Shallow module: interface is almost as complex as its implementation — callers learn a thing without getting much.
The Deletion Test
For each candidate: If I deleted this module, would the complexity it holds CONCENTRATE somewhere, or just MOVE elsewhere?
- Complexity vanishes → the module was a pass-through / shallow. It adds indirection without hiding complexity. Deepening candidate (delete it or deepen its interface).
- Complexity reappears across N call sites → the module is deep. It earns its existence by hiding complexity that would otherwise be duplicated. Leave it alone.
This verdict agrees with cc10x:codebase-design (canonical) and cc10x:architecture. Depth is leverage at the interface — not a lines-ratio.
| Smell | Usually |
|---|
Thin wrapper (doX(a){ return lib.doX(a) }) | Shallow |
| Pass-through layer (service maps 1:1 to repo) | Shallow |
| Near-duplicate helper (same intent, different name) | Shallow — consolidate first |
| Config/leverage module (small interface, branching/retry/state inside) | Deep — keep |
Diagnosis Flow (READ-ONLY)
- Enumerate exported surface (
name | file:line | signature)
- Flag shallow shapes
- Cross-reference near-duplicates (run duplicate detection mode first — consolidate before deepening)
- Run deletion test on each survivor
- Output: candidate list, not a refactor
Present Candidates Before Proposing Interfaces
Badge each candidate: Strong (unambiguous), Worth-exploring (real cost/risk), Speculative (hunch, low confidence).
For each: before/after sketch (current shallow interface vs proposed deeper one) with deletion test result. Then stop and ask which to pursue. Do not design interfaces for candidates the user hasn't chosen.
Two-Adapters Rule for Seam Placement
When designing the deeper interface, a port (an injected external dependency seam) needs two concrete adapters — e.g. a production transport and a test stand-in. An ordinary caller or a test exercising a public interface is NOT an adapter; the two-adapter rule is about ports, not every seam.
For a non-port seam (the common case for module deepening): the test must be able to reach the interface. If only production code crosses the seam and no test can reach it → the seam is a guess. Put the interface where a test can reach it, or don't introduce it. This aligns with cc10x:codebase-design — "one adapter means a hypothetical seam" applies to ports; internal seams need reachability, not a second adapter.
Handoff to BUILD
This skill ends at: chosen candidate + deeper-interface proposal + named seam. It does NOT edit code. The refactor routes through planner → BUILD workflow (builder → reviewer → verifier → doc-sync → memory). The deepening is verified: survivor interface has tests at its seam, every caller is repointed, suite + typecheck pass after merge.
Proposing is read-only; changing is gated.