Fell out of the corpus wave for #2729. It could not be seen before that wave, because no corpus language had both halves at once.
The measurement
galaxyscope.py L2228 adds a file's orphaned_logic to its api when the file is imported. It does not ask whether those functions were already counted as API surface by the language's api rule. So a function that is both declared public and uncalled contributes twice.
keyword-rosetta data/go/a.go, three functions, all three exported (func ProbeGlobals, func ProbeTest, func ProbeSafety), none called in-file, the file imported by main.go:
| column |
value |
function_count |
3 |
raw_arch_api (the api rule) |
3 |
raw_state_slop_orphans |
3 |
arch_api (what scoring reads) |
6 |
state_slop_orphans |
0 |
Three functions, six units of public surface. The same three identifiers are counted once as declarations and once as converted orphans.
Why it was invisible until now
Before the #2729 wave the corpus never planted an explicit public surface: raw_arch_api was 0 in 36 of 44 languages, so arch_api was entirely converted orphans and the two terms never overlapped. Now 30 languages declare their probes public and every one of them shows the doubling. Corpus-wide the effect is a bimodal arch_api: 6 where a language can declare visibility, 3 where it cannot but its orphans still convert, 0-1 where neither happens.
Why it matters
_calc_api_exposure and _calc_documentation read the adjusted api. On real code the same overlap is the common case, not the exception: a library's public functions are exactly the ones with no in-repo caller. Every exported-but-not-internally-called function in every imported file is currently scored as two public surfaces, so risk_api_exposure and risk_documentation are inflated most for the codebases that are most library-shaped.
It also lands on the rosetta bias table as 21 unexplained cells (risk_api_exposure in 18 languages, risk_documentation in livecode/ruby/tcl) whose measured input raw_arch_api sits exactly on the median — the deviation enters through a column the corpus cannot see, which is the same structural gap #2729 fixed for the gate.
Fix shape
Convert only the orphans the api rule did not already count. The recorder already has both numbers (raw_arch_api, raw_state_slop_orphans, #2536), so the cheap form is to credit max(0, orphans − rule_hits); the precise form is to exclude, per function, the ones whose declaration the api rule matched — spatial_maps has the api rule's match locations and the function spans are known, which is the same span data #2727 needs.
Either way this is a golden-master change: it moves arch_api on every imported file in the real-world corpus, and risk_api_exposure / risk_documentation with it. Verify the direction on language-crucible before blessing.
Part of #2669. Siblings: #2729 (the gate now asserts the rule, done), #2727 (the orphan census is name recurrence, not a call graph), #2730 (the api rule has no stated contract).
Fell out of the corpus wave for #2729. It could not be seen before that wave, because no corpus language had both halves at once.
The measurement
galaxyscope.pyL2228 adds a file'sorphaned_logicto itsapiwhen the file is imported. It does not ask whether those functions were already counted as API surface by the language's api rule. So a function that is both declared public and uncalled contributes twice.keyword-rosetta
data/go/a.go, three functions, all three exported (func ProbeGlobals,func ProbeTest,func ProbeSafety), none called in-file, the file imported bymain.go:function_countraw_arch_api(the api rule)raw_state_slop_orphansarch_api(what scoring reads)state_slop_orphansThree functions, six units of public surface. The same three identifiers are counted once as declarations and once as converted orphans.
Why it was invisible until now
Before the #2729 wave the corpus never planted an explicit public surface:
raw_arch_apiwas 0 in 36 of 44 languages, soarch_apiwas entirely converted orphans and the two terms never overlapped. Now 30 languages declare their probes public and every one of them shows the doubling. Corpus-wide the effect is a bimodalarch_api: 6 where a language can declare visibility, 3 where it cannot but its orphans still convert, 0-1 where neither happens.Why it matters
_calc_api_exposureand_calc_documentationread the adjustedapi. On real code the same overlap is the common case, not the exception: a library's public functions are exactly the ones with no in-repo caller. Every exported-but-not-internally-called function in every imported file is currently scored as two public surfaces, sorisk_api_exposureandrisk_documentationare inflated most for the codebases that are most library-shaped.It also lands on the rosetta bias table as 21 unexplained cells (
risk_api_exposurein 18 languages,risk_documentationin livecode/ruby/tcl) whose measured inputraw_arch_apisits exactly on the median — the deviation enters through a column the corpus cannot see, which is the same structural gap #2729 fixed for the gate.Fix shape
Convert only the orphans the api rule did not already count. The recorder already has both numbers (
raw_arch_api,raw_state_slop_orphans, #2536), so the cheap form is to creditmax(0, orphans − rule_hits); the precise form is to exclude, per function, the ones whose declaration the api rule matched —spatial_mapshas the api rule's match locations and the function spans are known, which is the same span data #2727 needs.Either way this is a golden-master change: it moves
arch_apion every imported file in the real-world corpus, andrisk_api_exposure/risk_documentationwith it. Verify the direction on language-crucible before blessing.Part of #2669. Siblings: #2729 (the gate now asserts the rule, done), #2727 (the orphan census is name recurrence, not a call graph), #2730 (the api rule has no stated contract).