feat(dead): Python earns the dead verdict via pythonVoice#124
Merged
Conversation
Populate Symbol.Visibility for Python (leading-underscore non-dunder name -> private, else public) and harvest the facts the dead-code voice reads: the broad mention set (shared walker), the getattr/setattr/hasattr dispatch set, the decorator / route / Django reach sets, and the __all__ export set. - visibility.go: the underscore convention, the only structural "not public API" signal Python offers, applied to every emitted symbol. - harvest.go: a PythonHarvestEmitter streaming py_decorated / py_routes / py_django / py_all_exports, plus mentions and reflective dispatch, all from one set of tree walks. Route/Django are classified by decorator last-name. - Python opts into MentionHarvester so the per-language soundness gate treats it as harvested. Golden fixtures gain the visibility field (additive only).
Aggregate the Python decorator / route / Django / __all__ reach sets streamed by the extractor and write them to flat sense_meta keys (py_decorated, py_routes, py_django, py_all_exports), alongside the existing per-language mention and dispatch sets. The dead-code Python voice reads these to keep a decorated / routed / Django-dispatched / declared-public symbol open-world. Updates the harvested-langs control test: Python now harvests mentions, so the non-harvesting negative control moves to a generic-spec Java file.
Register the pythonVoice in the arbiter: Python is the fifth language whose symbols can earn `dead`. Only an underscore-private function or method with no caller, no mention, and no invisible-reach idiom falls through to `dead`, mirroring the Ruby rule. Everything else raises a hand with an exact reason: - py_dunder (a __x__ protocol method, matched by pattern so module __getattr__ and dataclass __post_init__ are covered) - py_route / py_django (framework-dispatched handlers) - py_decorator (any other decorated symbol) - py_all_export (a name in __all__, overriding the underscore convention) - py_public (duck-typed reach), py_class / py_constant (never earn `dead`) The smoke fixture gains payments.py proving the two-sided gate end to end: one earned `dead`, plus py_dunder / py_decorator / py_route hand-raises and the core_name_mentioned soundness backstop. Golden regenerated.
Add the Python slice of the dead-code trust corpus: an underscore-private earned `dead`, a Django @receiver signal handler, a dunder protocol method, a model class, and an __all__ export. The binding precision gate asserts dead-precision == 100% with zero false `dead` through scan + the live arbiter, and a planted-false-dead self-test on the live signal receiver proves the gate measures rather than rubber-stamps.
Record the Python dead-code voice under Unreleased: underscore visibility, the py_* reason vocabulary, the harvest seam, and the binding gate (100% precision on the synthetic corpus plus a verified zero-false-dead run on the flask benchmark). Flags the Django gap honestly: no Django repo in the suite.
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 Python symbol with no resolved caller was reported as
core_no_language_voice— a single, uninformative "Sense can't reason about this stack" bucket. Python is a top-tier language for Sense, and an agent deserves precise, actionable reasons (and a sounddeadverdict where one is provable) the way Ruby, Go, Rust, and TS/JS already get.Summary
Python is now the fifth language whose symbols can earn the
deadverdict. An underscore-visibility pass plus a per-language harvest feed a newpythonVoicethat earnsdeadonly for an underscore-private function/method with no caller, no mention, and no invisible-reach idiom — and raises precise hand-raise reasons for everything else.Changes
Symbol.Visibilitypopulated for Python (leading-underscore non-dunder →private); a harvest streams the mention set, the getattr/setattr/hasattr dispatch set, the decorator/route/Django reach sets, and the__all__export set via a newPythonHarvestEmitter.sense_metakeys (py_decorated,py_routes,py_django,py_all_exports).pythonVoiceregistered in the arbiter with reasonspy_dunder,py_route,py_django,py_decorator,py_all_export,py_public,py_class,py_constant. Only underscore-private functions/methods fall through todead.Architecture Highlights
possibly_dead(duck-typed dispatch), so the eligible-for-deadset is small and intentionally private. The arbiter's broad mention gate is the soundness backstop — thedeadtier never rests on the underscore alone.__x__pattern rather than a fixed table, so the whole protocol surface (incl. PEP 562 module__getattr__, dataclass__post_init__) is covered without a table to maintain.Test Plan
go test ./...passesdead; planted-false-dead self-test fails the gate as expecteddead;py_dunder/py_decorator/py_route/core_name_mentionedhand-raises)0 → 1dead(verified genuinely unused), zero falsedead