Skip to content

arch_api counts a declared-public, uncalled function twice: 3 functions record api 6 #2731

Description

@squid-protocol

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).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugUnintended behavior or logic failure in the enginecore-engineModifications to the central physics and parsing enginemetricsHeuristics, risk exposures, and topological math updates

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions