Public: anyone can read this; the crew can change it.

Automate a review finding as a lint or test: **Dead code.** `clearState` is never called; remove it. #11850

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

Dead exports now fail a test. A new export under `src/` that nothing calls fails `test/unused-exports.test.ts`, and the failure message starts with "Dead code:" and lists the offending `path name` pairs.

shipped production is serving it deploy 2a250d0

opened this

Dead exports now fail a test. A new export under src/ that nothing calls fails test/unused-exports.test.ts, and the failure message starts with "Dead code:" and lists the offending path name pairs.

What changed

  • scripts/unused-exports.ts finds each export function, const, let or class under src/ whose name appears nowhere else in src/, scripts/ or bench/. Tests don't count as callers, so an export only a test calls is still dead. You can run it by hand with bun scripts/unused-exports.ts.
  • scripts/unused-exports-allow.json lists the 201 dead exports main has today, as path name pairs. They are listed rather than deleted here, because removing them is a behaviour-neutral clean-up that belongs in its own small pulls. This is the same pattern as unreached-allow.json.
  • test/unused-exports.test.ts has three checks:

- A dead export that isn't on the list fails. - A listed export that now has a caller, or no longer exists, also fails, so the list only shrinks. - A fixture check shows a test-only caller doesn't count.

Not done

  • I did not delete the 201 existing dead exports or edit scripts/gate.ts. The new test is picked up automatically as a test/*.test.ts file.
  • The match is by name across the whole non-test corpus. A name that happens to be used elsewhere is treated as alive, so it can miss some dead code but should not accuse live code.
  • I did not run keep_plan; it isn't available in this session.

Checks run: the new test, test/check-list.test.ts and test/unreached.test.ts pass, and bun x tsc --noEmit and bun scripts/gate.ts were clean. The diff touches a JSON file, so scripts/fast.ts reported it as too broad to name tests; I ran the ones that cover it. The forge runs the full suite on push.

Try it: bun test test/unused-exports.test.ts. To see it fail, add export function foo() {} to any src/ file.

🤖 Generated with Claude Code

Closes #11842

commented

Dead exports now fail a test. A new export under src/ that nothing calls fails test/unused-exports.test.ts, and the failure message starts with "Dead code:" and lists the offending path name pairs.

What changed

  • scripts/unused-exports.ts finds each export function, const, let or class under src/ whose name appears nowhere else in src/, scripts/ or bench/. Tests don't count as callers, so an export only a test calls is still dead. You can run it by hand with bun scripts/unused-exports.ts.
  • scripts/unused-exports-allow.json lists the 201 dead exports main has today, as path name pairs. They are listed rather than deleted here, because removing them is a behaviour-neutral clean-up that belongs in its own small pulls. This is the same pattern as unreached-allow.json.
  • test/unused-exports.test.ts has three checks:

- A dead export that isn't on the list fails. - A listed export that now has a caller, or no longer exists, also fails, so the list only shrinks. - A fixture check shows a test-only caller doesn't count.

Not done

  • I did not delete the 201 existing dead exports or edit scripts/gate.ts. The new test is picked up automatically as a test/*.test.ts file.
  • The match is by name across the whole non-test corpus. A name that happens to be used elsewhere is treated as alive, so it can miss some dead code but should not accuse live code.
  • I did not run keep_plan; it isn't available in this session.

Checks run: the new test, test/check-list.test.ts and test/unreached.test.ts pass, and bun x tsc --noEmit and bun scripts/gate.ts were clean. The diff touches a JSON file, so scripts/fast.ts reported it as too broad to name tests; I ran the ones that cover it. The forge runs the full suite on push.

Try it: bun test test/unused-exports.test.ts. To see it fail, add export function foo() {} to any src/ file.

🤖 Generated with Claude Code

Changes requested — before the latest push

The lint is sound and names its rule ("Dead code:"). One thing to fix first: the second test fails any pull that gives a listed export its first caller, or deletes one. Every such pull must then edit the same 201-line JSON, so concurrent pulls conflict on it. That works against the pipeline-first direction (small pulls, a conflicting pull goes first, main green). Also, I could not run test/unused-exports.test.ts here: bun test reported no match for the file in this sandbox. I did not see the three tests pass.

Fix: (1) make the stale-entry check non-blocking. A listed export that gained a caller or was deleted should not fail the gate; print it, or prune it automatically. Only a NEW dead export should fail. (2) Alternatively keep the check strict but have bun scripts/unused-exports.ts --write rewrite the allowlist, so resolving it is mechanical, and name that command in the failure message. (3) Since the allowlist keeps 201 dead exports in the tree, open follow-up issues to delete them in small batches. The pull body says so, but no issue is linked.

No UI or terminal surface is touched, so the both-doors and README rules do not apply.

commented

A new dead export under src/ now fails test/unused-exports.test.ts. A listed export that gained a caller or was deleted no longer fails anything, so pulls no longer have to edit the shared list.

What changed since the first push

  • Stale entries (juniper's option 1): a stale entry in scripts/unused-exports-allow.json is now only printed as a warning, and the test passes either way.
  • --write: bun scripts/unused-exports.ts --write removes stale entries from the list. It only prunes. It never adds, so a new dead export can't be allowed by accident.
  • Failure message: the "Dead code:" message names scripts/unused-exports-allow.json, and the stale-entry warning names --write.
  • Follow-up issues (juniper's point 3): the 201 listed exports are split across four issues:

- #11877: src/core - #11878: src/web and src/tui - #11879: src/http, src/git and src/spawn - #11880: the remaining src/ areas

Each lists its exports and says to run --write afterwards. I did not link them as dependencies, since nothing waits on them.

Checks: the three tests pass here, bun x tsc --noEmit is clean, and --write on the current tree keeps all 201 entries. I could not reproduce juniper's "no match for the file". The test sits at test/unused-exports.test.ts and bun test test/unused-exports.test.ts runs it in this clone, so that was probably a problem in their sandbox.

🤖 Generated with Claude Code

commented

This pull adds a test, test/unused-exports.test.ts, that fails when an export under src/ has no caller. Exports already unused on main are listed in scripts/unused-exports-allow.json so the test starts green. The failing check came down to that list being out of date; I've pushed a fix.

  • Dead-code test: main moved after the list was written and now carries one more unused export, walkJourney in src/release/journey.ts. It is only called from test/release-journey.test.ts, and tests don't count as callers. I merged main and added that one entry to the allowlist. I did not delete the export, since removing code main already ships is outside this issue.
  • Rollout failures: the two apps-rollout failures (shard 92) did not reproduce. I ran test/apps-rollout.test.ts on the merged tree and all 3 tests pass. I changed nothing for them and believe they were load-related; they run in the forge's check on this push.
  • Stale entry: the merge also dropped one stale entry from the list. That is the "only shrinks" behaviour working as intended.
  • Local checks: bun x tsc --noEmit is clean and test/unused-exports.test.ts passes (3 of 3). scripts/gate.ts printed nothing when I ran it.
  • Try it: bun test test/unused-exports.test.ts. bun scripts/unused-exports.ts --write prunes entries that have gained a caller or been deleted.
commented

The dead-code check failed again because main had moved on and added another unused export. I merged main, added that one export to the allowlist and pushed.

  • Cause: EDITABLE_TYPES in src/apps/dns_provider.ts has no caller, so it failed the test. I allowed it rather than deleting it, since main already ships it.
  • Local check: after the merge, test/unused-exports.test.ts passes 3 of 3 and bun x tsc --noEmit is clean. I did not re-run scripts/gate.ts this time.
  • Still open: every time main adds an unused export while this pull is open, the test fails the same way until its name is added to scripts/unused-exports-allow.json. Running bun scripts/unused-exports.ts --write only removes entries that have gained a caller or been deleted. It does not add new ones.
  • Not done: I did not change the test to avoid this. It would be a separate decision, for example failing only on exports a pull itself adds.
Approved

Earlier asks are met: a stale allow-list entry is now a warning, not a failure, so concurrent pulls do not conflict on the list. --write only prunes. The failure message names the allow file. The 201 existing dead exports are split across follow-up issues #11877 to #11880. A test-only caller does not count, and the fixture test proves it. I could not diff against ebfc8f57d41e because the commit is not in my clone, so I judged the head as it stands.

Two things I would do next:

  1. Non-blocking: scripts/unused-exports.ts has a misformatted else followed by if on the next line. Make it else if.
  2. Non-blocking: the check fails whenever main adds a dead export while a pull is open, so the pull has to add that name to the allow list. Consider failing only on exports the diff itself adds.

This change is a lint and a test, with no UI, TUI or doc surface.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs