/which-cleanup
Route a completed change to a small, proportionate closeout roster. This is the
third default router alongside /which-shape and /which-skill:
/which-shapechooses the operating loop;/which-skillchooses a task skill;/which-cleanupchooses post-change checks and guards.
Do not execute the recommended skills. Return exact on-demand guide and tool paths so the calling agent can load only what it needs. For non-trivial closeout, prefer a fresh non-context sub-agent per independent read-only lens; keep mutations serial.
Run
From a Codex project installation:
cd .agents/skills/which-cleanup
python3 scripts/route.py <scope args> --project-root <host-root> --json
Supported scopes:
<path> [<path> ...]
--staged
--changed-from <ref>
--commit <sha>
--range <a..b>
With no scope flag, inspect the working-tree, staged, and untracked file lists. The portable router requires only Python’s standard library and Git when a diff scope is used.
--scope-mode auto preserves each scanner's current behavior. For a Git
selector, --scope-mode changed-files analyzes each selected file in full;
--scope-mode diff-lines lets line-local scanners report only findings that
intersect new-side changed lines. Explicit paths use paths. Project-wide
analysis is intentionally delegated to project-capable scanners rather than
silently broadening this change-bounded router.
Interpret the result
Honor these fields:
scope_band:trivial,small,medium, orlarge, based on changed-file count. It controls roster width, not correctness or risk by itself.resolved_paths: the exact bounded paths considered.scan_request: the reusable selector, content basis, path/change facts, and changed-line ranges. Pass this exact object to selected scanners; do not resolve the Git selector again per recommendation.recommendations[]: skill, reason, primary on-demandhandoffwith the shared source-inventory path and manifest-backed language/fact/outcome capability row, and anoptional_installresult used only when the user requests ambient installation. The command is present only for closures with passed selected-install evidence; unvalidated closures are explicit.recommendations[].scan: requested/effective mode, diff semantics, and an explicitready,adapted,widened,degraded,unsupported,unsafe, orunavailablestatus.widenedpreserves file/multi-site/project obligations that cannot truthfully be hunk-clipped. Do not run an unsupported or unsafe mode.source: the canonical repository and conventional skill/tool roots.limitations: what portable mode deliberately does not infer.
The universal closeout floor is test-obligation drift, comment drift, and regression prevention. Wider changes additionally surface duplication, omnibus-module, and incomplete-sweep checks. This is a conservative router, not proof that every recommendation applies.
Handoff
For each relevant recommendation:
- Skip it explicitly if it is irrelevant to the actual change.
- For a tiny check, read only its
handoff.guidespaths directly. - For a non-trivial independent check, create a fresh non-context sub-agent
with the bounded
scan_request, recommendationscancontract, reason, and returned guide/tool paths. The adapter says whether changed paths seed wider analysis or the request triggers a project scan; do not hunk-clip those findings. - Follow that skill’s own support and runtime claims; a location is not a claim that the tooling is language-neutral or independently installable.
When a selected producer returns a normalized finding artifact, apply committed human review memory after detection and scope attribution:
cd .agents/skills/which-cleanup
python3 scripts/reviewed_findings.py \
--decisions <project-root>/.engineering/quality/finding-decisions.jsonl \
filter --artifact <normalized-findings.json> --output <actionable.json>
The canonical ledger is .engineering/quality/finding-decisions.jsonl in the
host project. An unchanged false-positive, accepted-debt, or
intentional-exception decision is hidden from the default actionable list but
remains counted; add --show-reviewed to inspect it. Changed evidence, changed
subject identity, incompatible fingerprint semantics, malformed decision data,
and recurrence after fixed all remain visible. Record a judgment only after
human review:
python3 scripts/reviewed_findings.py \
--decisions <project-root>/.engineering/quality/finding-decisions.jsonl \
record --artifact <normalized-findings.json> --subject <stable-subject> \
--disposition false-positive --rationale <why> --reviewer <identity> \
--source-scan <scan-id>
The decision ledger stores hashes and bounded identity/location fields, never the raw evidence or full source body. Failures, incomplete/unsupported results, unlocated findings, and file/project obligations cannot be dismissed through this line-finding mechanism.
Use optional_install only when the user explicitly chooses ambient
installation.
Skip irrelevant recommendations explicitly. Keep mutations serial even when multiple read-only checks can run independently.
Boundaries
- Advisory only; never edits production files or runs the selected skills.
- Does not load all skill bodies or a repository-wide execution runtime.
- Does not promise subsystem-specific routing without a separately installed project profile or task skill.
- The source checkout retains richer historical closeout scripts for its own development, but they are not part of this portable default path.
- The thin library bootstrap only materializes the source outside discovery; it is not a dispatcher, workflow coordinator, package manager, or trust layer.
Failure handling
- No changed paths: report no recommendation and stop successfully.
- Invalid or conflicting scope flags: surface the usage error.
- Git unavailable or an invalid/empty diff scope: return a structured resolution failure; ask for explicit in-project paths rather than scanning or silently broadening to the working tree.
- Recommended skill unavailable or unsupported: report the source location and limitation; do not invent or inline its behavior.
Files
.claude/skills/which-cleanup/
├── SKILL.md
└── scripts/
├── route.py # portable stdlib-only installed router
├── scan_request.py # paths, Git changes, and new-side line ranges
├── scope_modes.py # per-scanner effective-mode projection
├── finding_envelope.py # normalized artifact + shared scope adapters
└── reviewed_findings.py # append-only review memory + fail-open filter