sweep
Use when the user wants to find code that has quietly stopped being used — types /sweep, asks for a "dead code sweep", "repo entropy", "what can we delete", "orphan files", "unused code", or asks to review a sweep ledger. Adjudicates candidates a deterministic scanner already produced; it never deletes anything and never re-derives findings from raw source. Pairs with ponytail, which gates what gets written rather than what rots.
- License 许可
- MIT
- 最近更新
- 2026-11-09
- Stars
- ★ 6
- Forks
- 1
01 前言 Front Matter
- name
- sweep
- license
- MIT
- metadata
- { "author": "ai-playbook", "version": "1.0" }
- description
- Use when the user wants to find code that has quietly stopped being used — types /sweep, asks for a "dead code sweep", "repo entropy", "what can we delete", "orphan files", "unused code", or asks to review a sweep ledger. Adjudicates candidates a deterministic scanner already produced; it never deletes anything and never re-derives findings from raw source. Pairs with ponytail, which gates what gets written rather than what rots.
03 SKILL.md 正文
sweep — adjudicate repo entropy, never delete it
You are reviewing a ledger, not searching a repository.
scripts/sweep_scan.py has already done the deterministic half: it resolved
imports through the project's own module-resolution config, applied the
entry-point conventions of the declared frameworks, and produced one row per
unreachable file with typed evidence. Your job is the half a scanner cannot do —
decide whether each candidate is genuinely dead, or is reached by something
static analysis cannot see.
Read this before you adjudicate anything
Your failure mode is laundering. Measured on a real repository: a naive
resolver that ignored tsconfig.json path aliases reported 89 live files as
dead, including the application's own layout and auth provider. A model
adjudicating that output would have produced 89 fluent, confident, wrong
rationales — converting a resolver bug into reasoned ledger rows that look like
analysis.
So the first question is never "is this file dead?" It is "do I believe this scan at all?"
- The scanner refuses to emit a ledger unless the consumer's declared
probes— files known to be live — all read as reachable. If you are holding a ledger, that gate passed. - A passed gate is not a clean bill of health. It proves the resolver works
for the paths the probes exercise, and nothing more. On the same repository, a
second run reported ten live modals in one directory as orphans: the framework
preset listed
page.tsxbut notpage.js, so the routes above them were not entry points. All six probes were.tsxfiles reached through.tsximporters, so every one of them passed. If the repo is half-migrated, or spans languages, ask whether the probes actually cover the mechanism a cluster depends on. - If findings cluster suspiciously (an entire directory, every file of one extension, a whole framework's worth of components), suspect the RESOLVER before you suspect the repo. Say so, and stop. A missing preset or an unread path alias explains a cluster; twenty independent developers abandoning one directory does not.
The mechanisms that have actually produced false orphans, in order of how often
they bite: an unread path alias, a missing entry-point extension, an importer
the config never parsed, a resolution rule outside the language (a webpack
resolve.alias, a COPY in a Dockerfile, a dynamic import()).
What you may and may not do
The ledger schema (schemas/schema-sweep-manifest-v1.json) enforces most of
this; the point of restating it is that you should not want to do it either.
- You may not delete, move, or edit any file. Producing an adjudicated
ledger is the whole of the job. Execution is a separate, explicit human step.
A previous auto-delete in this playbook destroyed 623 lines of live code, from
a
--quiethook, and went unnoticed for three weeks. - You may not remove a row. There is no
deletedecision. The weakest thing you can do to a finding isdismiss, which KEEPS the row at Tier 3 with a written rationale, so the next sweep can see it was already considered. - You may not edit
evidence. It was written once, by the detector. You consume it; you do not revise it. - You may not escalate to Tier 1. Delete authority requires a human. Your escalations cap at Tier 2/3.
- Every decision other than
confirmrequires a rationale, and the rationale must name the MECHANISM, not the feeling. "Looks unused" is not a rationale. "Loaded bynext/dynamicinapp/(ops)/page.tsx:26" is.
When a finding carries unfinished_commitments
Stop. This row does not mean what the others mean.
evidence.unfinished_commitments is a count of undischarged obligations the
scanner found in the file — unticked task boxes, TODO, "still required", "MUST
TEAR DOWN". A stranded document that owes work is not entropy. It is an
obligation with no watcher, and it is more dangerous than a dead file, because
deleting it destroys the only record that the work exists.
Measured on a real repository, two of these were live: a PROGRESS.md
unreferenced at the repo root for four weeks, owing a teardown of seed data in a
customer's production Google Workspace; and a security remediation checklist with
eight unticked P0 items including credential rotation.
So:
- Never
confirmsuch a row for removal. The executor refuses it anyway, but you should not want to: your recommendation would be to delete a promise. - Read the commitments and report them as the finding. The interesting output is not "this file is unreferenced", it is "these N obligations are unowned, and here is what they are". Quote them.
- The fix is to MOVE the obligation, not the file — into a ticket, a runbook, or the deferred-items ledger, wherever this project actually watches work. Once it has an owner, the marker leaves with it and the next scan clears the row honestly.
- An unticked box that is genuinely done and was never ticked is still a finding: the record is wrong, and a record nobody maintains is one nobody can trust.
How to adjudicate one finding
Work from the evidence, then look for the thing the scanner structurally cannot see. In order:
- Read
evidence.locationsandevidence.search_scope. "Nothing references it" is only as strong as where the scanner looked. If the subject lives outside the searched scope, that is adismisswith the scope named. - Search for the subject by NAME as well as by path, and read the hits.
A dynamic import, a lazy
next/dynamic, a string-built module name, a registry keyed by string, or a plugin entry point all reference a file without an import statement. Any of these →dismiss(ordowngradeif partial), naming the file and line. - Check whether it is a deliberate surface: a public API re-export, a fixture, a template rendered by path, a script an operator invokes, a documented escape hatch. These are alive by contract, not by import.
- Check for a twin. A file with a same-named sibling elsewhere is often
half of a duplication where only one copy is wired. That is a REAL finding
and usually a more interesting one than a lone orphan: the two copies drift,
and fixes applied to the live one never reach the dead one. Link them with
related_ids. - Only then confirm. A
confirmmeans: the evidence stands, and you looked for the four things above and found none.
Cluster related rows with related_ids so one root cause reads as one problem
rather than as ten.
What you write
Add the adjudication block to each row, in place, leaving evidence
untouched:
{
"decided_by": "llm",
"decision": "dismiss",
"tier": 3,
"decided_at": "2026-08-02T11:04:00Z",
"model": "<your model id>",
"rationale": "Reached by `next/dynamic(() => import('./BulkDeleteModal'))` in app/(app)/(modules)/group-management/page.tsx:31, which the static resolver cannot follow. Not an orphan; the scan is correct that no import statement names it."
}
Then report to the user, in this order: how many findings, how many you confirmed, how many you dismissed and why in one line each, and any cluster you suspect is a resolver artefact rather than real entropy.
Close with the one question the ledger cannot answer: which of the confirmed findings the user actually wants to act on. Do not act on it yourself.
When to fire
- User types
/sweep, or asks for a "dead code sweep", "repo entropy check", "what can we delete", "orphan files", "unused code". - User asks you to review or adjudicate an existing sweep ledger.
- A monthly cadence reminder fires. Entropy is a slow variable; scanning it more often than it changes is how a detector gets disabled.
When NOT to fire
- Mid-feature. A file added in the current branch and not yet wired is work-in-progress, not entropy. Sweep against a merged tree.
- On a dirty worktree.
scan.dirty_worktreein the ledger tells you; a half-written import can make a live file look orphan. Discount accordingly and say so. - For the decidable axes. Unused dependencies, unwired capabilities and disk
residue have deterministic rules that run at zero token cost
(
repo-hygiene,capability-wiring). Do not re-derive their findings by hand — read their output.
See also
docs/concepts/code-entropy.md— the five axes and why only two need you.schemas/schema-sweep-manifest-v1.json— the ledger contract, including every constraint above expressed as schema.docs/rules/cleanup-zombies.rule.md— the tier semantics, and the executor that acts on a ledger once a human authorises it.skills/ponytail/SKILL.md— the preventive twin: it gates what enters a diff, this adjudicates what rotted after it landed.
04 同仓库更多 Skills
ponytail
Use when the user wants the laziest solution that actually works — types /ponytail, says "be lazy", "lazy mode", "simplest solution", "minimal solution", "yagni", "do less", or "shortest path", or complains about over-engineering, bloat, boilerplate, or unnecessary dependencies. Supports intensity levels lite, full, ultra (default). Pairs with caveman, which compresses prose, not code.
ponytail-review
Use when the user wants a code review focused exclusively on over-engineering — says "review for over-engineering", "what can we delete", "is this over-engineered", "simplify review", or invokes /ponytail-review. Complements correctness/security review; this one only hunts complexity in the current diff. Gated by the ponytail review_ponytail component.
ponytail-debt
Use when the user wants to harvest every `ponytail:` comment in the codebase into a tracked debt ledger — says "ponytail debt", "/ponytail-debt", "what did ponytail defer", "list the shortcuts", "ponytail ledger", or "what did we mark to do later". One-shot report, changes nothing. Gated by the ponytail debt_ponytail component.
ponytail-audit
Use when the user wants a whole-repo audit for over-engineering — says "audit this codebase", "audit for over-engineering", "what can I delete from this repo", "find bloat", or invokes /ponytail-audit. Like ponytail-review but scans the entire tree, not a diff. One-shot report, applies no fixes. Gated by the ponytail audit_ponytail component.
openspec-propose
Propose a new change with all artifacts generated in one step. Use when the user wants to quickly describe what they want to build and get a complete proposal with design, specs, and tasks ready for implementation.
openspec-explore
Enter explore mode - a thinking partner for exploring ideas, investigating problems, and clarifying requirements. Use when the user wants to think through something before or during a change.