# Find Dead Code in a Rust Workspace

Yes. An agent builds your Rust workspace with and without its features, traces every public item to its callers, and reports what can go, with semver impact.

[Run this on Rush](https://cloud.getrush.ai/run/find-dead-code/rust/), in a Project that has your repository.

## What the agent does

The agent starts from the lints the workspace already enforces, because they decide what the compiler will and will not report. In a workspace like `rust-lang/mdBook`, the root `Cargo.toml` lists the `mdbook` binary crate plus `crates/*`: `mdbook-core`, `mdbook-driver`, `mdbook-html`, `mdbook-markdown`, `mdbook-preprocessor`, `mdbook-renderer`, `mdbook-summary`, `mdbook-compare` and `xtask`, all on edition 2024 with `rust-version = "1.88.0"`. `[workspace.lints.rust]` sets `unreachable_pub`, `missing_docs` and `rust_2018_idioms` to warn, and `[workspace.lints.clippy]` allows everything but `correctness` and `complexity`, and `.github/workflows/main.yml` runs `cargo clippy --workspace --all-targets --no-deps -- -D warnings`, so an item the compiler can prove unused within one crate is already a CI failure. Dead code here is the kind the compiler cannot see: a `pub` function in a library crate that no other crate in the workspace calls.

So the agent builds the call graph itself. It runs `cargo build --workspace --all-targets` and `cargo clippy` first to confirm the baseline is clean, then `cargo test --workspace --no-default-features` and `cargo build --workspace --no-default-features`, because the root crate's `default = ["watch", "serve", "search"]` features gate `notify`, `axum`, `tokio` and `tower-http` behind `dep:` entries, and an item that is only reachable with a feature on is not dead. For every `pub` and `pub(crate)` item in the library crates it searches the whole workspace, including `tests/testsuite`, `examples` and `guide/guide-helper`, for a use, and it reads the one existing `#[allow(dead_code, reason = "currently only used in search")]` in `crates/mdbook-html/src/html/tree.rs` as a marker the maintainers already left for themselves.

It separates what is removable from what is published. `mdbook-core`, `mdbook-preprocessor` and `mdbook-renderer` are the API that third-party preprocessors and renderers compile against, and CI runs `cargo semver-checks --workspace`, so an unused public function there is not dead code but a breaking change, and the report says so instead of proposing to delete it. Items that are `pub` only because a sibling module once needed them get the `pub(crate)` or private visibility that `unreachable_pub` would then enforce; test helpers in `tests/testsuite` that no test calls, example code in `examples` that nothing references, and dependencies in a crate's `Cargo.toml` that no `use` or path mentions, checked with `cargo machete`, go in their own sections.

The front end is part of the workspace too. `package.json` runs ESLint over `crates/mdbook-html/front-end/*js` and the `.js.hbs` templates, so the agent runs `npm run lint` with `no-unused-vars` reported rather than ignored, and looks for theme files under `crates/mdbook-html/src/theme` that no Handlebars template or `include_str!` references. The result is `dead-code.md` with every item, its file and line, the search that found no caller, the feature set it was checked under, and whether removing it is a patch, a minor or a breaking change. The agent changes no code and opens no pull request.

## Start it with one API call

`POST https://api.prix.dev/o/{org}/sessions`

```json
{
  "agent": "claude",
  "input": "Report the dead code in this Rust workspace without changing anything. Read the root Cargo.toml including its workspace lints and features, every crate's Cargo.toml under crates, .github/workflows/main.yml, package.json and rustfmt.toml first. Run cargo build --workspace --all-targets and cargo clippy --workspace --all-targets --no-deps -- -D warnings to confirm the baseline, then build and test with --no-default-features and record which items are only reachable with the watch, serve or search features on. For every pub and pub(crate) item in the library crates, search the whole workspace including tests, examples and guide for a use and list the ones with none. Mark items in crates that third-party preprocessors or renderers compile against as public API whose removal cargo semver-checks would flag, and propose a visibility reduction or a deprecation instead of deletion for those. Run cargo machete or an equivalent for unused dependencies per crate, run npm run lint over the front-end JavaScript with unused variables reported, and look for theme files nothing references. Write dead-code.md: the commands and their output, then one section each for safely removable items, items to make pub(crate) or private, unused dependencies, unused test and example code, front-end findings, and public API that looks unused but must stay, each entry with file and line, the evidence, and the semver impact. Change no code and open no pull request.",
  "resources": [
    {
      "url": "https://github.com/your-org/your-app"
    }
  ]
}
```

1. **Get an API key.** In the console, open Settings, then API keys, and create a key for your org. Send it in the Authorization header as a Bearer token.
2. **Send the request.** POST the body in request.json to https://api.prix.dev/o/{org}/sessions, with your repository as the resources URL. The run starts in its own cloud sandbox.
3. **Read what came back.** Follow the run on its live stream or wait for the run.completed webhook, then read the result and its trajectory.

## What comes back

What comes back is `dead-code.md` as an artifact on the session, with a download link the session read returns. It opens with the commands the agent ran and what they printed, so a reader knows the workspace built cleanly with the default features and without them before any item was judged, and which items changed status between the two builds.

The body is six sections. Items that can be deleted today, with file and line and the `rg` search that found no caller. Items that should lose `pub` so the existing `unreachable_pub` lint keeps them honest. Dependencies no crate uses. Test helpers and example code nothing runs. Front-end JavaScript and theme files nothing references. And the public API in `mdbook-core`, `mdbook-preprocessor` and `mdbook-renderer` that has no caller inside the workspace but is there for external crates, listed so nobody deletes it by mistake, with the `cargo semver-checks` consequence spelled out.

A reviewer reads the first two sections against the code, decides which removals to take, and sends a follow-up message to the same session asking for a pull request with those deletions, since the sandbox still has the workspace built and `cargo test --workspace --locked` ready to run on the result.

## Run it on every first morning of the month

In the Project that has your repository, add an automation with this prompt and a `schedule` trigger, `cron` set to `0 7 1 * *` with your `timezone`, which fires at seven on the first day of each month. Each run starts in the Project's repository at its default branch, writes a fresh `dead-code.md`, and the artifact is on the session for the team to read.

Since the automation's run has no key behind it, the report is collected from the session rather than from a key webhook; a team member reads the artifact in the console or a script lists the Project's sessions and downloads it.

## Why run it on Rush?

Finding dead code in a Rust workspace is not one command. The compiler reports unused private items, `unreachable_pub` reports over-exposed ones, and nothing reports a public function that no crate calls, so the search is a build under each feature set plus a grep per item plus a judgment about what is published. On a laptop that work is abandoned halfway. In a Rush sandbox the agent runs every build, keeps the output in the transcript, and finishes the list.

The report is checkable. Each entry carries the search that found no caller and the feature set it was built under, and the public-API section stops a well-meaning cleanup from becoming a breaking release that `cargo semver-checks` catches only after the fact. The same prompt runs on any Rust workspace without changes.

## Frequently asked questions

### Why doesn't the compiler already report this?

The `dead_code` lint works within one crate and treats every `pub` item of a library as used, because anything outside the crate might call it. In a workspace of nine crates most code is `pub` for the benefit of a sibling crate, so the compiler is silent about a function whose only caller was removed last year. The agent does the cross-crate search the compiler does not.

### Will it propose deleting public API?

No. Items in the crates that third-party preprocessors and renderers depend on are listed separately as public API with no internal caller, with a note that CI's `cargo semver-checks` would flag their removal as breaking. The report proposes deprecation or a visibility change for those, and deletion only for items the workspace alone can reach.

### What about code behind features?

The agent builds and tests twice, with the default `watch`, `serve` and `search` features and with `--no-default-features`, and records which items are only reachable with a feature on. Those are not dead, and the report says which feature keeps each one alive.

### Does it cover the JavaScript and theme files too?

Yes. The workspace's `package.json` already lints the front-end files under `crates/mdbook-html/front-end`, so the agent runs that with unused variables reported and checks that each theme file is referenced by a template or an `include_str!`. Findings there sit in their own section.

### Can it make the removals?

Not in this run; the report changes nothing. A follow-up message to the same session naming the sections to apply asks the agent to delete those items, run `cargo test --workspace --locked` and `cargo clippy` with warnings denied, and open a pull request.
