Task: Delete provably unreachable code

SkillDev tools

Autonomous maintenance routine that deletes provably unreachable code

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Task: Delete provably unreachable code skill

What this skill tells your AI

The instructions your AI receives, as published by bex-co/beancount-io in .agents/skills/routine-dead-code-removal/SKILL.md and read by ahel’s review.

Usage: /routine-dead-code-removal [package-or-path]

Run the target package's dead-code detector, prove each hit is truly unreachable, delete it (with the tests and fixtures that only existed for it), make the package's checks pass, and ship each removal via /ship.

Contract

Read .agents/skills/routine-shared/contract.md first — it defines preconditions, scope resolution, verify gates, the ship protocol, the budget, and universal STOPs. This file adds only what is specific to dead code. Floor even without it: never ship red; one finding per ship; anything in a DO_NOT_DO.md is a hard STOP.

Workflow

1. Scope

Resolve scope per the contract. With no argument, prefer running scripts/lint-deadcode.sh from the repo root once as a survey, then pick the one package with the most promising hits.

2. Discover

cd into the package, then run its detector:

  • JS/TS packages (mobile, dashboard, backend-cluster/backend-v2, backend-cluster/ledger, backend-cluster/agent-box) → yarn lint:deadcode (Knip; config in the package's knip.jsonc)
  • cli/make deadcode (Vulture; high-confidence only)

Also legitimate finds while reading nearby code: commented-out blocks, if (false)-style branches, early-return shadowed code. A detector hit is a candidate, not proof.

3. Prove it

For every candidate, sweep for references the detector can't see before touching anything:

  • Grep the whole package for the symbol/file name as a string — dynamic imports, registries, config-referenced entries, test fixtures.
  • Expo Router is file-based: files under mobile/app/ are routes reachable by navigation and deep links even with zero imports — never a dead-code finding.
  • Codegen inputs (GraphQL documents, codegen.ts, apollo.config.json) and build/tooling entry points (babel.config.js, scripts referenced from package.json) count as reachable.

A confirmed false positive is its own legitimate finding: add a scoped ignore to the package's knip.jsonc (with a short reason) instead of deleting, so the detector stays trustworthy.

4. Fix

  • Prefer the safe fixers where they apply: yarn lint:deadcode:fix / make deadcode-fix — then review the resulting diff before keeping it (root CLAUDE.md rule). Discard anything the proof step didn't cover.
  • Delete manually what the fixers can't reach (commented-out blocks, unreachable branches).
  • Delete tests and fixtures whose only purpose was exercising the removed code — they travel with it.

5. Verify

Run the owning package's full gate from the contract's table, plus a clean re-run of the detector to confirm nothing was orphaned by the deletion.

6. Ship

Compose /ship for this one removal. Loop back to step 2 while candidates and budget remain.

What NOT to do

  • Never delete a branch that is dead only because a feature flag holds one value — that is routine-shipped-feature-inliner's job.
  • Never delete one of two live duplicate implementations — that is routine-dup-unifier's job (it repoints callers first).
  • Never touch the CLI's vendored fava code, codegen output, or lockfiles.
  • Never trust a Knip/Vulture hit without the reference sweep — file-based routes and dynamic lookups are exactly where detectors lie.

Arguments

$ARGUMENTS

Signals

GitHub stars
267
Forks
30
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
routine-dead-code-removal
Source
github.com/bex-co/beancount-io