Skip to content

The dead-pub audit: 411 items, 2 dead, ~2 100 lines of over-visibility

Snapshot db64923, 2026-10-01. Tool: tools/dead_pub_audit.py (stdlib only, one pass, no cargo). Nothing was deleted.

“Cut hard, but cut on evidence.” This is the evidence, and the answer is not the answer anyone wanted: there is almost nothing dead. Of 411 pub items across our 23 crates, 2 are dead outright — 60 lines. The real finding is a different one, and it is worth more than the deletion would have been:

verdict items lines what it means
live — a caller outside its own file 287 7 210 nothing to do
needs-eyes — the name is shared by another item 32 292 cannot be called dead by name search; see §4
demote — reached only from its own file or a test 87 2 085 pub is over-visible; the body is live. The cut here is pub → pub(crate), ~60 characters
cfg-only — no caller in the default feature set 2 21 a note, not a death (NOISE (c))
dead-documented 1 12 named in a doc, called by nothing
DEAD 2 60 no code caller, no doc mention, no test

The scale-down of “масштабное урезание”: the honest number is 60 lines of dead code, not thousands. Everything else is either load-bearing or a visibility label. §7 says where the mass actually is — and it is not in pub items.

tools/dead_pub_audit.py finds every ^pub fn|struct|enum|trait|const at column 0 (top-level API only; impl-block methods are indented and are counted through the type or trait name that carries them), then looks for that name in every .rs file in the repo — the 6 workspace crates and the 20 library crates, plus vendor/dormouse-fused/{bench,benches,tools} and our own tools/. vendor/cubecl-fix and vendor/cubek-fix define none of ours but are read as callers. One name-search pass per file; ~5 s cold.

A mention is split three ways, because the three answers are three different verdicts:

  • in another file, in code → a real caller (calls)
  • in its own defining file, in code → pub is over-visible, the body is live (demote). This split matters: it is the difference between “delete 439 lines” and “delete the word pub”. The first version of this script got it wrong and reported bidiag_host — 439 lines of Golub-Kahan bidiagonalization — as dead, when host_svd.rs:52 calls it.
  • in a comment → documented, not called (dead-documented)

A self-use below the file’s first #[cfg(test)] is counted as a test use, so “only the tests reach it” is a visible column and not silently demote.

One scope fact worth its own line: the doc column reads .rs comments only. A name documented in a docs/**.md file is invisible to it — which is why cholesky_host_par reads doc = 0 although two other audits written the same day name it (§1). A .md mention is corroboration, never a caller.

The four noise classes, named in the script’s header:

  • (a) a pub use re-export counts as a use. The burn-fused facade (vendor/dormouse-fused/burn-fused/src/lib.rs, 60 lines, generated by tools/gen_facade.py) re-exports all 20 crates. Nothing in either manifest depends on it — the facade is a CI artifact, not a product dependency, so for our purposes it is not a caller either way. It is library-api, not a candidate.
  • (b) trait methods are reached through the trait name. A pub trait whose every method has zero direct mentions is normal and is not reported dead: the trait itself is the item.
  • (c) #[cfg(feature = …)] is a note, not a death. cfg-only means “no caller under the default feature set”. Two items are in that class (§6).
  • (d) a shared name can never be called dead. new, forward, seam_counts appear in several items; every one of them is needs-eyes, never DEAD. 32 items, listed in §4.

Two more limits, both erring toward not deleting: cfg_gated stops at the first blank line when walking up to an enclosing mod, and item_extent brace-balances without parsing string literals. The first makes a gated item read as live; the second under-counts. Seven probes were checked by hand and all seven extents match the closing brace exactly.

item file:line lines why it is dead
burn_sct::qr::cholesky_host_par vendor/dormouse-fused/crates/burn-sct/src/qr.rs:562 49 no caller anywhere. Its serial f32 twin cholesky_host (same file, :536, pub(crate)) IS called — qr_cuda.rs:578 — so the f64 threaded variant lost its consumer and kept its name. Cross-validated: two independent same-day audits reached the same verdict — docs/research/2026-10-01-spectral-inventory.md:78 (“(d) DEAD: pub, zero callers”) and docs/reviews/spectral-stack-audit-2026-10-01.md:62
burn_sct::qr_cuda::cube_of_int vendor/dormouse-fused/crates/burn-sct/src/qr_cuda.rs:78 11 no caller anywhere. The sibling cube_of (:55) has 147 callers in the same file. cube_of_int is the int-tensor variant of a helper nobody needed at int

Both are in burn-sct, whose whole surface is library API (§5), so the verdict is delete rather than demote — but see §8 for why one of them is worth a second reader: cholesky_host_par is the only unsafe block in the audit’s top 10.

DELETED 2026-10-01 — function and doc line together, in the config-seams lane (docs/reviews/config-seams-2026-10-01.md); grep train_eval_split crates/ tools/ is 0. The record below is the finding as made.

dormouse_data::train_eval_split (crates/dormouse-data/src/lib.rs:192) is called by nothing and mentioned exactly once, at lib.rs:16:

//! - [`train_eval_split`] returns a stable held-out tail so eval sees unseen

That sentence is false about our own trainer. The trainer takes an explicit --eval file and streams it separately (dormouse-data/src/lib.rs:577, “Stream over an explicit file list (e.g. a held-out split)”), so the crate-level doc advertises a function nothing calls and a mechanism the trainer does not use. This is ADR-0019’s shape in prose: the reader is told an arm ran when it did not. Verdict: delete the function and the doc line together — the doc line is the more valuable half of the deletion, because it is the thing that is wrong.

3. demote: 87 items, 2 085 lines — the actual finding

Section titled “3. demote: 87 items, 2 085 lines — the actual finding”

Not dead; over-visible. pub on an item only its own file reaches says “this is API” when it is not. The cut is one word each, so this row is a hygiene answer, not a size answer. The top of it:

item file:line lines reached from verdict
burn_sct::bidiag_host host_svd.rs:168 439 host_svd.rs:52 (same file) pub(crate)
burn_gdn2::chunk_wy_forward_loop forward.rs:350 297 forward.rs:154 + 2 tests pub(crate) — but see §8
burn_gdn2::chunk_wy_forward_batched forward.rs:195 132 forward.rs:152 + 2 tests pub(crate)
burn_sct::dbdsqr host_svd.rs:612 94 host_svd.rs:60 pub(crate)
burn_attnres::depth_attend_backward_cuda fused_attnres.rs:481 93 3× same file pub(crate)
burn_attnres::two_phase_attend lib.rs:618 91 tests only (lib.rs:721,733, below #[cfg(test)] at :710) pub(crate)
burn_spectral::polar_orthogonalize lib.rs:246 62 11 in-file + 9 in-file tests keep pub — the doc calls it out as an oracle seam
burn_muon_plus::ns_combine_cuda fused_kernels.rs:89 44 3× in-file tests already documented dead — see §5
burn_spectral::polar_orthogonalize_batched lib.rs:316 43 lib.rs:374 pub(crate)
burn_kda::apply_rope lib.rs:259 37 2 in-file + 9 in tests/kda_rope.rs keep pub — the doc says why: “Public because it is the oracle’s seam”

Two of the top ten are already justified in their own doc comments (apply_rope, polar_orthogonalize). That is the library API working as intended, and it is the reason the demote row is not a delete list.

4. needs-eyes: 32 items, 292 lines — regex cannot decide these

Section titled “4. needs-eyes: 32 items, 292 lines — regex cannot decide these”

A name shared with another item makes the count an upper bound belonging to all of them, so none is ever called dead. The heavy ones: seam_counts (6 items), reset_seam_counts (6), ternarize (2), quant_format (2), pool_stats (2), init_pools (2), memory_cleanup (2), kda_seam_counts (2), eggroll_mutate (2), note (2), calls (2), reset (2).

Nearly all of these are counters or resets — a seam function, i.e. code whose only job is to be counted (the exceptions are ternarize and quant_format, duplicated quantizer names, not seam counters). The pub is deliberate. Do not read this row as a finding; it is the list of names a name-based audit cannot resolve, and a type-based one (cargo +nightly udeps / -Zunused-crate-dependencies) would.

5. library-api: legitimately “dead” inside this repo

Section titled “5. library-api: legitimately “dead” inside this repo”

A technology library’s pub is a promise to a future caller, not a claim about today’s call graph. These are not deletion candidates and the audit says so:

  • burn-muon-plus::ns_combine_cuda (fused_kernels.rs:89, 44 lines) — the crate’s own module doc already states it (burn-muon-plus/src/lib.rs:53): “ns_combine_cuda has no production caller: its only one was the factored NS branch in MuonPlus::orthogonalize, deleted as unsatisfiable … It is kept because its own test in this module still pins the kernel against the tensor-op expression it computes.” This is the precedent for the whole library-api class: dead in production, pinned by a test, kept on purpose, and said so where a reader will find it. Any future deletion in this repo should either cite this shape or give a better one.
  • The burn-fused facade — 60 generated lines, nothing depends on it, and that is correct: it is what tools/gen_facade.py --check diffs in CI.
  • burn-es, burn-parcae, burn-ptrn, burn-eggroll — whole crates with no manifest naming them (§7).

6. cfg-only: 2 items, 21 lines — notes, not deaths

Section titled “6. cfg-only: 2 items, 21 lines — notes, not deaths”
item file:line the gate reading
dormouse_train::cubecl_client crates/dormouse-train/src/lib.rs:319 #[cfg(feature = "cuda")] parked, not dead. Its own doc: “factored out because the graph seam needs it too and two copies of a backend downcast is how they drift.” A sibling worktree (wt/cuda-graph) is building the consumer. Do not delete — the doc already says what would break
burn_dspark::accept_rate_loss vendor/dormouse-fused/crates/burn-dspark/src/lib.rs:309 #[cfg(feature = "training")] training is enabled — crates/dormouse-core/Cargo.toml:23 requests it. So the gate is open and there is still no caller. The nearest verdict is demote/test-pin, but the module doc at lib.rs:7 claims it is “matched against the official …” reference, which is the claim a reader would check. Reported, not judged

The pub-item cut is 60 lines. The unused code is measured differently, and it is not small:

Four library crates, 1 388 lines of src/, that no product manifest names as a dependency. The only manifest that names them is the burn-fused facade’s own (vendor/dormouse-fused/burn-fused/Cargo.toml:85-95), and the facade is the CI artifact above, not a consumer:

crate src/ lines referenced by another crate
burn-ptrn 399 nothing
burn-es 345 nothing
burn-eggroll 327 nothing (one name-only mention, in burn-es)
burn-parcae 317 nothing

That is 23× the dead-pub figure, and it is a whole-crate decision, not an item decision. .bulba/plan.md’s question 4 (“the unwired library, start deleting now?”) is the right question; this audit is one line of its answer. A fifth, burn-fused itself (60 lines), is a CI artifact by design.

burn-sct is the other concentration: 593 of its lines sit under non-live items, 533 of them in host_svd.rs — a 745-line vendored LAPACK transcription. It is a library crate with a live CUDA QR consumer, so this is library-api again, but it is the one crate where a reader should look twice.

8. Verdicts on the top 10 by lines, checked by eye

Section titled “8. Verdicts on the top 10 by lines, checked by eye”

Each was read, not just counted. delete means “remove the item”; demote means “pub → pub(crate), body stays”.

# item lines verdict why
1 burn_sct::bidiag_host 439 demote called at host_svd.rs:52 from svd_host. A Golub-Kahan bidiagonalization with 439 lines and one caller in the same file. Not dead; mislabelled
2 burn_gdn2::chunk_wy_forward_loop 297 demote, blocked called at forward.rs:154, and tests/ops_batched_diff.rs:367 imports it. pub is honest — an A/B seam between the loop and the batched arm. Do not touch without the owner’s A/B decision
3 burn_gdn2::chunk_wy_forward_batched 132 demote same pair, same reason. The two together are the loop-vs-batched A/B, which is an open experiment, not dead code
4 burn_sct::dbdsqr 94 demote called at host_svd.rs:60. LAPACK dbdsqr shifted-QR iteration, vendored
5 burn_attnres::depth_attend_backward_cuda 93 demote 3 in-file callers. AGENTS.md §2.2 records AttnRes as wired-but-unwired-for-Backward-Block; the file is load-bearing
6 burn_attnres::two_phase_attend 91 demote reached only by two tests below lib.rs:710. The one demote in the top ten that is a real visibility bug: a 91-line public API no production code calls
7 burn_rmsnorm::MIN_FUSED_D 1 keep Not dead: MIN_FUSED_D is the documented refusal threshold from the 2026-09-30 RMSNorm finding (AGENTS.md §3.3). It is a named constant precisely so the threshold is readable. It reads demote — one self-use inside the cuda module (fused.rs:215) plus 7 test uses — not a deletion candidate in any reading
8 burn_spectral::polar_orthogonalize 62 keep 11 in-file + 9 test uses, and lib.rs:1641 names it as the reference retract is pinned against
9 burn_sct::cholesky_host_par 49 delete 0 callers. And it is the only item in the top ten containing raw-pointer unsafe (qr.rs:588-602, disjoint-row-slicing across std::thread::scope, hand-rolled Send-ness argument). Deleting it removes ~30 lines of unsafe from a crate that has one other use of it. That is the strongest single argument in this report, and it is an argument about unsafe, not about line count
10 burn_kda::apply_rope 37 keep 9 uses in tests/kda_rope.rs, and the doc says why: “Public because it is the oracle’s seam” — it is compared against FLA’s rotary_embedding_ref. §1.4’s evidence rule in the code

Net of the top ten: 1 delete (49 lines, and it is the unsafe one), 5 demote, 3 keep, 1 blocked on an open A/B. Not one of the ten is the “large cut” anyone imagined, and the two largest (1 and 2) are A/B seams and a vendored algorithm respectively.

  • It is a name search, not a type analysis. cargo udeps resolves paths; this does not. A pub fn reached only through a macro expansion, a trait object, or a #[cfg] this script does not parse reads as dead. The demote split exists because that failure mode was observed and fixed once already (§3, item 1).
  • impl-block methods are out of scope by construction. The surface a type-based tool would find is not measured here.
  • 32 needs-eyes items are unresolved, not cleared (§4).
  • It says nothing about dead code with a live caller — a function called from one place that itself is dead. That is a reachability question, and this is not the instrument for it.
  • Snapshot db64923. Active lanes are changing these files. Re-run python3 tools/dead_pub_audit.py and diff; the columns are stable for exactly that purpose.

60 lines of dead pub in a repo that looks like it has thousands, and the real unused mass — 1 388 lines across four whole library crates — is not visible to a pub-item audit at all. Cut the 60, demote the 87 pubs, and decide the four crates as crates.