Principles
Start from first principles: confirm the page's purpose, the facts it depends on, and the business invariants that must remain true. Do not start from mechanical deduplication or deleting optional checks.
Good cleanup removes redundant sources of truth and meaningless branches while preserving real loading, empty, error, permission, and lifecycle states.
Use this skill when cleanup may change user-visible behavior. It is used to distinguish meaningless defensive code from real lifecycle, permission, error, and empty states.
Do not treat comments as a cleanup target. Only update them when they are wrong, outdated due to code changes, or describe deleted behavior.
Do not stop after cleaning up one component or helper. For the page or flow requested by the user, you must trace the full flow boundary and impact surface before reporting completion.
This skill is one of three cleanup skills:
- Use
page-flow-cleanup-auditto handle user-visible flows, state ownership, and business invariants. - Use
necessary-code-auditto determine whether wrappers, compatibility, fallbacks, default values, and defensive code are necessary. - Use
unused-code-auditto prove whether deleted helpers, exports, and files still have consumers.
Workflow
1. Define the flow boundary
- Name the page/file/feature and its user-visible purpose.
- Identify entry points: user entry points, main components or handlers, hooks, query helpers, mutation helpers, stores, dialogs, and external consumers.
- Distinguish the main flow from adjacent reusable business surfaces. Do not merge them just because the UI looks similar.
- If the request includes "no backward compatibility needed" or "delete defensive code," apply it only to compatibility layers or guards for impossible states, not to real nullable business states.
- Build an impact surface map: routes, shared packages, stores, query keys, API clients, analytics events, native/web consumers, tests/scripts, and downstream apps/packages touched by the flow.
Depth requirement: before editing, inspect every file within the boundary; for every modified shared helper/type/store/query, inspect representative downstream consumers. Do not rely on a single grep result or one obvious call site.
2. Find the Source Of Truth
Trace state in the following order:
- Server/API contract。
- Query key and query function.
- Query cache invalidation after mutation.
- Local store or component state.
- Derived computation.
- UI adapter and presentation.
Red flags:
- Multiple queries fetch the same business fact.
- A store and React Query both claim ownership of the same remote state.
- A mutation directly invalidates the original key in multiple components.
- A "lightweight query" copies the same API source, creating a second cache.
- Component-local count/status comes from a stale or independent source.
Recommended shape:
- One query helper owns one remote fact.
- mutation calls a shared invalidation helper.
- Unless the backend has a real count endpoint, count and label are derived from the canonical query.
- response type expresses the guarantee after the boundary, rather than adding a guard in the UI.
3. Classifying Duplicate Logic
Only extract logic that is deterministic and business-equivalent.
Suitable for extraction:
- Product/category grouping.
- Filtering and sorting.
- Time series or portfolio return derivation.
- Eligibility determination.
- Query-key projection helper。
- Type-level request/response boundary helpers.
Do not extract:
- Layout, density, collapse behavior, mobile/desktop interaction, toolbar composition, or route-specific loading decisions.
- UI code that looks similar but has different context or user flow.
- Component wrappers that only hide two call sites without reducing business complexity.
4. Classifying Defensive Code
It can be deleted as meaningless when:
- A prop or boundary type already guarantees the value exists.
- A boolean parameter is always passed as a constant.
- A helper only wraps an empty object or an obvious literal.
- A fallback only serves an old data format, and the user has explicitly stated it is no longer supported.
as anyIt hides a type problem that could be solved by tightening the boundary.- Optional chaining that violates a required prop only weakens errors.
It must be retained when:
- The domain type is genuinely optional, such as optional expiration, optional strategy item, missing profile data, or an unselected row.
- The branch represents a real loading, empty, error, permission, or access state.
- The fallback is an intentional user experience degradation, such as falling back to link sharing after screenshot sharing fails.
- The value comes from external input, and its absence is still valid business behavior.
If the value is only optional before the success boundary, create a narrower response type instead of adding a guard in the UI.
5. Safe Cleanup Order
- First delete outdated source-of-truth paths.
- Add or reuse a shared invalidation helper.
- Extract pure computation helpers.
- Replace duplicate local derivations with the helper.
- Tighten request/response boundary types.
- Delete meaningless guards,
as any, empty fallback helpers, and constant parameters. - Simplify the query key by keeping only fields that affect the result.
- After each source or helper deletion, search again for leftovers.
Do not normalize data "just in case". Keep edits surgical and tied to the audited flow.
Do not spend effort on comment cleanup unless the comments are outdated, misleading, or orphaned by code edits.
6. Completion Gate
Before reporting completion, you must:
- Re-check the full flow boundary, not just the edited files.
- Search for deleted symbols, old query/store names, old source paths, duplicate literals,
as any, constant parameters, and optional chaining that conflicts with required types. - Confirm the remaining defensive branches are all real loading, empty, error, permission, lifecycle, external-input, or domain-nullable states.
- Confirm the modified shared helper/type already has downstream validation coverage.
- Explain the candidates intentionally left untouched and the reasons.
Evidence chain
Structural evidence:
- Inventory of pages or feature files.
- import/export searches for component, hook, helper, store, query, mutation, and route entrypoint.
- manifest, public entrypoints, generated files, type declarations, and framework conventions.
- Use language tooling when available: compile diagnostics, test coverage, dependency analysis, or IDE references.
- Check representative callers of shared helpers, stores, query keys, and public types.
- Check the workspace manifest and select the affected downstream checks.
Text residue search:
- Old store names.
- Old query helpers and keys.
- Deleted helper names.
- Constant parameters.
as any。- Optional chaining on required props.
- Compatibility comments or legacy format branches.
Search definition locations and every possible consumer root directory, not just the edited file or directory.
Verification
Run the narrowest check that covers the boundary of the change:
- Run the package build when exported queries/types change.
- Run the package typecheck when local components/helpers change.
- Run the typecheck of the affected app or consumer when public imports are touched.
- When cleanup changes user-visible state, query behavior, permissions, or mutation invalidation, run a flow-level smoke test or focused test.
- Check for residue of deleted symbols and old source paths
rg。 git diff --check。
If downstream consumers read generated artifacts or declaration files, rebuild in dependency order first, then assess downstream type errors.
Report
Report:
- Source-of-truth changes.
- Extracted pure helpers and deleted duplicate logic.
- Deleted defensive code, as well as the real nullable states that were retained.
- Tightened type boundaries.
- Residue searches and verification commands.
- Untouched unrelated dirty files.
When rebuttal is needed, state it clearly: "This is not defensive code, but a real business empty state/error state/permission state/lifecycle state."