fix(makefile): credit .PHONY external entry points as api surface, not tech debt (#2904) - #2970
Merged
Merged
Conversation
…t tech debt (#2904) A makefile's callable units are invoked EXTERNALLY -- a human typing `make all`, CI, a Dockerfile -- never by an in-repo caller or an import. So a makefile's popularity is structurally 0 and galaxyscope's Contextual Baseline Fix (which credits an imported file's orphans to api surface) could never reach it: every `.PHONY:` entry point fell through to `risk_tech_debt`, reading e.g. 99.33% debt on an 18-line makefile whose three targets are correct, complete and idiomatic. Add a tier-3 branch to the Contextual Baseline Fix, opt-in per language via a new closed `export_visibility` registry key (mirroring #2806's `invocation_model`): a language whose export construct is narrow and curated enough to name EXTERNAL entry points declares `export_visibility: external_entry_points`. Only makefile opts in -- its `.PHONY:` (`_visibility_export_list`) names exactly the external interface, unlike a JS/TS `export` that decorates every symbol and would blind the #2774 dead-code census. For an opted-in file, the tier-3 branch clears its DECLARED entry-point orphans (`api_declared_orphans` -- the orphans whose name sits on an api-rule line) from the `unreferenced_by_name` census, and heals their per-function `usage_status`. Crucially it moves ONLY the declared portion: a genuinely internal, UNdeclared orphan target (never `.PHONY`, no in-repo caller) stays real dead weight -- cpython's Makefile.pre.in drops from 57 to 21 orphans, not to 0, keeping its 21 undeclared internal targets as debt. `api` is NOT re-credited (a declared entry point's declaration line is already an api hit, so re-crediting would double-count), and the honest census survives in #2536's `raw_pre_adjustment` snapshot -- only the debt/surface classification moves, never the raw count. Golden masters regenerated (both legs): the diff is scoped to makefile Tech Debt Exposure / Unreferenced By Name cells and their rollups, with no file crossing the density aperture (none newly parsed or excluded). rosetta stays 46/46 (its makefile fixture's targets are all referenced, so nothing moves) -- no paired corpus rebless owed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PXAjGXaZKr2Ge6jjZshPRD
Contributor
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.
Closes #2904.
Problem
A makefile's callable units are invoked externally — a human typing
make all, CI, a Dockerfile — never by an in-repo caller or animport. Its popularity is therefore structurally 0, so galaxyscope's Contextual Baseline Fix (which credits an imported file's orphans to api surface — tier 2) can never reach it. Every.PHONY:entry point falls through torisk_tech_debt(e.g. an 18-line, fully-correct makefile reading 99.33% debt).Fix — tier-3 external-entry-point rescue (one classification-layer change, per #2812 rule 4)
export_visibility(detector.py), mirroring orphaned_logic reports 100% dead code for five languages whose invocation model never names the callee — the family #2727 closed without covering #2806'sinvocation_model:standard(default, census-everything) vsexternal_entry_points. Only makefile opts in — its.PHONY:(_visibility_export_list) names exactly the external interface, unlike a JS/TSexportthat decorates every symbol and would blind orphan census: anexport -f nameline counts as a use, so a declared-public uncalled function can never be reported dead #2774.spliceemits a per-fileexports_are_external_entry_pointsflag into the payload.galaxyscope.py), gatedelifon the tier-2 branch not firing: for an opted-in file it clears the declared entry-point orphans (api_declared_orphans) fromunreferenced_by_nameand heals theirusage_status..PHONY, no in-repo caller) stays real dead weight — cpython'sMakefile.pre.indrops 57 → 21 orphans, not to 0, keeping its 21 undeclared internal targets as debt.apiis left unchanged.raw_pre_adjustmentsnapshot still holds the honest orphan count; only the debt/surface classification moves._calc_api_exposurepopularity == 080% dampener is kept — tier-3 does not touchmeta["popularity"]. Lifting it (via a synthetic popularity) is a possible follow-up, noted in makefile .PHONY entry points read as tech debt: the Contextual Baseline Fix has no external-invocation tier #2904.Verification
test_export_visibility_contract_2904.py(closed-set + opt-in family, mirroring the orphaned_logic reports 100% dead code for five languages whose invocation model never names the callee — the family #2727 closed without covering #2806 contract) andtest_galaxyscope.py::...tier3...(bootos all-declared → census 0, api unchanged; a mixed file keeps its undeclared internal orphan as debt; a non-opted-in language untouched).bless_scopefootprint = 13 substantive diffs, leaf keysTech Debt Exposure/Unreferenced By Name/tech_debt/avg_tech_debtonly, no file newly parsed or excluded (rule did not cross the density aperture).ruff_audit/mypy_audit/dead_key_audit--ci: no new findings. Fullpytest tests/: 8738 passed (only the pre-existingtest_security_auditor.pyxgboost-env failures, identical onorigin/main).Out of scope (follow-ups noted in #2904)
risk_tech_debtover-sensitivity at small unit counts (3-of-4 in an 18-line file).popularity == 0api-exposure dampener for tier-3 via a synthetic popularity signal.🤖 Generated with Claude Code