The README lists an honest blind spot:
- Names public only inside an extension, which is a separate module.
True as written. But it stops one sentence short of the useful part: there is a pattern that
avoids the blind spot entirely, and this package already uses it. A reader who hits that bullet
today learns their extension is invisible, and not that they can simply make it visible.
The package demonstrates the workaround on itself
test_surface has exactly one method, and that method lives in ext/ExperimentalAPITestExt.jl.
It is nonetheless fully accounted for, because the parent module declares the function and its
docstring, and only delegates the implementation:
# src/ExperimentalAPI.jl
public test_surface
"""
test_surface(m::Module; skip = Symbol[], outputlevel::Int = 0) -> Audit
...
"""
function test_surface end # <- declared here, implemented in the extension
Measured on abe736b:
julia> ExperimentalAPI.audit(ExperimentalAPI)
public : 17
documented : 17
unaccounted : Symbol[]
dangling : Symbol[]
test_surface in surface? true
test_surface documented? true
is it defined in the ext? true
ext module name ExperimentalAPITestExt
So the blind spot is narrower than the bullet implies. It is not "extensions are invisible" — it
is "a name that exists only inside an extension, and is never declared by the parent, is
invisible". Every extension API that follows the declare-in-parent, implement-in-extension
convention — which is the convention Julia already pushes you toward, since the parent needs the
function object for the extension to add a method to it — audits normally.
Suggested change
A paragraph under "What the audit cannot see", next to the existing bullet, saying roughly:
- the blind spot applies to names the parent never declares;
- the fix is
function f end plus public f (or export f) in the parent, with the method in
the extension;
- and that this package does exactly that for
test_surface, so the claim is checkable rather
than aspirational.
That turns the bullet from "here is a hole" into "here is a hole and here is how not to fall in
it", without changing any code.
Not proposed here
Making audit walk Base.get_extension and fold loaded extensions into the surface. That would
make the result depend on which optional dependencies happen to be loaded in the auditing
session, so the same package would audit differently in CI and in a user REPL — a worse property
than the documented gap. The declaration pattern gets the coverage without that.
The README lists an honest blind spot:
True as written. But it stops one sentence short of the useful part: there is a pattern that
avoids the blind spot entirely, and this package already uses it. A reader who hits that bullet
today learns their extension is invisible, and not that they can simply make it visible.
The package demonstrates the workaround on itself
test_surfacehas exactly one method, and that method lives inext/ExperimentalAPITestExt.jl.It is nonetheless fully accounted for, because the parent module declares the function and its
docstring, and only delegates the implementation:
Measured on
abe736b:So the blind spot is narrower than the bullet implies. It is not "extensions are invisible" — it
is "a name that exists only inside an extension, and is never declared by the parent, is
invisible". Every extension API that follows the declare-in-parent, implement-in-extension
convention — which is the convention Julia already pushes you toward, since the parent needs the
function object for the extension to add a method to it — audits normally.
Suggested change
A paragraph under "What the audit cannot see", next to the existing bullet, saying roughly:
function f endpluspublic f(orexport f) in the parent, with the method inthe extension;
test_surface, so the claim is checkable ratherthan aspirational.
That turns the bullet from "here is a hole" into "here is a hole and here is how not to fall in
it", without changing any code.
Not proposed here
Making
auditwalkBase.get_extensionand fold loaded extensions into the surface. That wouldmake the result depend on which optional dependencies happen to be loaded in the auditing
session, so the same package would audit differently in CI and in a user REPL — a worse property
than the documented gap. The declaration pattern gets the coverage without that.