feat(dead): Go language voice earns the dead verdict#121
Merged
Conversation
Add the scan-time harvest seam the Go dead-code voice stands on. The Go extractor now streams its own broad mention set (the per-language soundness gate), reflective dispatch targets (reflect.MethodByName / FieldByName string args and tagged struct-field names), and cgo //export function names, all in a single tree walk per file. A new extract.CgoExportEmitter plus the scan collector/harness persist //export names to a flat cgo_exports sense_meta key. - golang: HarvestsMentions() opts the language into harvested_langs, so its symbols stop failing closed at the per-language soundness gate - golang: blank const/var declarations (`const _`, `var _`) are no longer emitted as symbols, so a blank is never a dead candidate - scan: cgo_exports written per full scan, unioned like the other name sets
Go becomes the second language whose symbols can earn `dead`. The goVoice earns `dead` only for an unexported func/method/type with no caller, no mention, and no invisible-reach idiom; everything else stays possibly_dead with an exact reason: go_init, go_interface (indexed or stdlib interface methods), go_cgo, go_generated, go_exported (staticcheck U1000 flags only unexported, so exported stays open), and go_const (an iota anchor must not be cut). Soundness is structural: Go visibility is capitalized-means-exported, already recorded on every symbol. - dead: register goVoice; populate Facts.InterfaceMethodNames and Facts.CgoExportNames; the core_reflection hint is now language-neutral - dead: init/Init are no longer treated as constructors — the Go voice owns init (go_init) so it surfaces with an accurate "runtime-invoked" hint - dead: testdata/ fixtures are excluded from candidacy, matching the Go toolchain's universe so fixtures are never reported as removable - smoke fixture + golden prove the two-sided gate end to end (a dead helper earns dead; an init and an interface method stay possibly_dead)
Add a compiler-grade trust gate for the Go voice: scan a repo with Sense, run `staticcheck -checks U1000`, and assert Sense's `dead` set is a subset (zero false dead). staticcheck knows the full type system and build tags Sense lacks, so the binding invariant is Sense `dead` (Go) ⊆ U1000, not equality. The runner matches on (file, bare-name) to survive line/column drift between the tools and injects its command runner so all exit shapes test deterministically without the binary installed; the live end-to-end check skips cleanly when it is absent.
Record the Go dead-code voice under Unreleased: the earned-dead candidate, every possibly_dead reason, the harvest seam, the staticcheck U1000 oracle, and the testdata/ exclusion. Update the existing dead-code breaking-change note from "only Ruby ships a voice" to "Ruby and Go".
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Every Go symbol Sense reported was
possibly_deadwith reasoncore_no_language_voice— the honest default for any stack with no registered voice. That surrenders the one language where adeadverdict can be made airtight: Go is statically typed and compiled, its visibility is structural (capitalized = exported), and the toolchain already ships an oracle for exactly this class (staticcheck'sU1000, unused unexported symbols). An agent asking "what is safe to delete in this Go package?" deserved a precise answer, not a wall ofpossibly_dead.Summary
Add a Go language voice so Go becomes the second language whose symbols can earn the
deadverdict, with the verdict validated againststaticcheck U1000as a compiler-grade oracle.Changes
Harvest seam (
internal/extract,internal/scan)reflect.MethodByName/FieldByNameargs, tagged struct-field names), and cgo//exportnames.extract.CgoExportEmitter; the scan persists//exportnames to a flatcgo_exportsmeta key.const _,var _) are no longer emitted as symbols (a blank is never an unused-symbol finding).Go voice (
internal/dead)goVoiceearnsdeadonly for an unexported func/method/type with no caller, no mention, and no invisible-reach idiom. Everything else stayspossibly_deadwith an exact reason:go_init,go_interface(indexed + stdlib interface methods),go_cgo,go_generated,go_exported,go_const.Facts.InterfaceMethodNamesandFacts.CgoExportNamesare populated; the sharedcore_reflectionhint is now language-neutral.init/Initare no longer treated as constructors — the voice ownsinit(go_init) so it surfaces with an accurate "runtime-invoked" hint.testdata/fixtures are excluded from candidacy, matching the Go toolchain's universe so fixtures are never reported as removable.staticcheck oracle (
internal/dead/eval)staticcheck -checks U1000, and asserts Sense'sdeadset is a subset (zero falsedead). Matches on (file, bare-name) to survive line/column drift; injects its command runner so all exit shapes test without the binary installed.Breaking Changes
dead. Previously every Go symbol waspossibly_dead/core_no_language_voice. No wire-schema change.Test Plan
dead; aninitand an interface method staypossibly_deadwith exact reasons.go_*code; planted false-deadis detected by the oracle gate.dead, validated againststaticcheck U1000(sense: 2 dead, 100% recall; gin: 0).go test ./...passes;make cigreen (0 lint issues).