Skip to content

fix(connectivity): stop the note promising dead_code rows are safe to remove - #637

Merged
zzet merged 1 commit into
zzet:mainfrom
tiendungdev:fix/connectivity-note-removal-verdict
Aug 20, 2026
Merged

fix(connectivity): stop the note promising dead_code rows are safe to remove#637
zzet merged 1 commit into
zzet:mainfrom
tiendungdev:fix/connectivity-note-removal-verdict

Conversation

@tiendungdev

Copy link
Copy Markdown
Contributor

Problem

Every connectivity_health response ships a note that ends:

This is distinct from dead code (analyze kind=dead_code), which reports symbols with zero INCOMING usage edges — genuinely unreachable code that is safe to remove.

The two doc-comments feeding it agree — internal/analysis/connectivity.go says "the code is unused and can be removed", internal/mcp/tools_analyze_connectivity.go says "a real finding to act on (delete it)".

That contradicts the vocabulary this analyzer explicitly says it stays in lockstep with. ClassifyZeroEdge reaches the opposite conclusion from the same zero-incoming shape:

  • likely_unused"this is evidence of no callers, not proof: confirm with a text search for the symbol name before removing it" (extraction_gap.go:296)
  • coverage_incomplete"not as proof the symbol is unused or safe to remove" (extraction_gap.go:310)

And FindDeadCode's own contract claims no more than "all symbols with zero incoming calls or references, excluding entry points, test functions, exported symbols, and user-excluded patterns" (deadcode.go:233). The removal verdict is layered on top of that, and only in these three places.

Why the hedge is load-bearing

Two ordinary situations leave a live symbol with exactly the zero-incoming shape dead_code reports on:

  1. Ambiguous member calls. resolveMethodCall deliberately declines to bind rather than misattribute to a same-name candidate — "ambiguous stays unresolved rather than misattributing to a same-name". Every losing candidate keeps zero incoming edges.
  2. Name-only matches, which never earn a trustworthy usage edge.

Measured on a 2,475-file Go repository (gortex v0.63.7+0988412): analyze dead_code returned 4 rows and git grep refuted 3 of them. Two were methods reached through a nested field selector (p.provider.recordSubmit(...), called 16 lines below their own definition); one was a method value (var cryptorandRead = randReader). A reader who trusted the note would have deleted three working symbols.

Note the callers caveat next door is already correctly hedged — this is the one surface that still isn't.

Change

Reword all three sites to state the signal and withhold the verdict. The isolated-vs-dead-code distinction the note exists to draw is unchanged; only the removal promise goes.

Test

TestConnectivityNote_MatchesSharedZeroEdgeStance pins the property, not the prose, so a future rewording is free but a re-promotion is not:

  1. builds the dead-code shape (one structural defines edge, zero incoming usage),
  2. asserts as a positive control that CaveatForZeroEdge for that same symbol is likely_unused and still says "not proof",
  3. asserts the report actually carries connectivityNote,
  4. asserts the note keeps naming dead_code and its INCOMING signal,
  5. asserts it contains no removal verdict (safe to remove / safe to delete / can be removed / can be deleted).

Sabotage-verified: restoring the old sentence fails the test on assertion 5 — should not contain "safe to remove". Reverting makes it green again.

Limitations I'd rather declare than have found

  • The forbidden-phrase list is a closed set of four. A future note could promise removal in wording the list does not cover; the test would not catch that. I chose an explicit list over a fuzzier check because a false positive here would block unrelated rewordings.
  • Assertions 3–5 bind the note only through GraphConnectivity's return value. The MCP handler's own doc-comment is changed for coherence but is not test-bound (it is a comment).
  • The measurement above is evidence for why the hedge matters; it is not exercised by the test, which uses a synthetic fixture.

Verification

  • go test ./internal/analysis/ -run 'TestConnectivityNote|TestGraphConnectivity' -count=1 — 6/6 pass, plus the sabotage run above.
  • go build ./internal/mcp/ ./internal/analysis/ — clean.
  • golangci-lint run --timeout=10m ./internal/analysis/... ./internal/mcp/...0 issues.
  • git diff --check clean; diff carries no CR bytes. (gofmt -l flags all 83 files in internal/analysis on my Windows clone, untouched files included — a local core.autocrlf artifact, not this change.)
  • No .md / .json / .yaml mirrors the old wording; no test referenced the note before this one.

Windows 11, go1.26.6, branched from main at a71cbedb.

… remove

The `note` shipped on every connectivity_health response ended with
"genuinely unreachable code that is safe to remove", and the two
doc-comments feeding it said the same ("the code is unused and can be
removed", "a real finding to act on (delete it)").

That contradicts the shared vocabulary this analyzer says it stays in
lockstep with. ClassifyZeroEdge reaches the opposite conclusion from the
same zero-incoming shape: likely_unused is "evidence of no callers, not
proof: confirm with a text search for the symbol name before removing
it", and coverage_incomplete is "not as proof the symbol is unused or
safe to remove". FindDeadCode's own contract claims no more than "zero
incoming calls or references" — the removal verdict was added on top of
it, and only here.

The hedge is load-bearing, not stylistic: an ambiguous member call is
deliberately left unresolved rather than misattributed to a same-name
candidate, and a name-only match never earns a usage edge. Both leave a
live symbol with the exact zero-incoming shape dead_code reports on, so
a reader who trusts the note deletes working code. Measured on a 2.5k-file
Go repository, three of four dead_code rows were refuted by grep; two were
methods reached through a nested field selector.

Reword all three sites to state the signal and withhold the verdict. The
isolated-vs-dead-code distinction the note exists to draw is unchanged.

The new test pins the property rather than the prose: it builds the
dead-code shape, asserts the shared caveat for that same symbol still says
"not proof", then asserts the note keeps naming dead_code and its INCOMING
signal while carrying no removal verdict. Restoring the old sentence fails
it on the "safe to remove" assertion.
@tiendungdev

Copy link
Copy Markdown
Contributor Author

Heads-up so this doesn't cost you a debugging round: the red lint is a transient module-proxy fetch, not this change.

internal/parser/forest/dump_kinds_test.go:38:12: could not import
github.com/alexaandru/go-sitter-forest/pug (…: read
"https://proxy.golang.org/github.com/alexaandru/go-sitter-forest/pug/@v/v1.9.1.zip":
stream error: stream ID 67; INTERNAL_ERROR; received from peer) (typecheck)

1 issues:

internal/parser/forest/ is untouched here — this PR only edits internal/analysis/connectivity.go, its test, and a comment in internal/mcp/tools_analyze_connectivity.go. CI on main at a71cbedb, the exact base of this branch, is green.

Locally, golangci-lint run --timeout=10m ./internal/analysis/... ./internal/mcp/... (v2.11.4, same as CI) reports 0 issues. A re-run of the job should clear it; I can't trigger one from a fork.

@zzet
zzet merged commit e8d60b8 into zzet:main Aug 20, 2026
17 of 18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants