Summary
run_api_docs's docstrings check requires a docstring for every name in public_api_names(pkg), including re-exported module bindings. A package that re-exports a dependency's module (export SciMLBase) cannot satisfy that check: Julia resolves Docs.Binding(pkg, :SciMLBase) to SciMLBase.SciMLBase, so any @doc written in the re-exporting package is stored in the dependency's doc registry, not its own. The only ways to make the check green are (a) a module docstring in the dependency, or (b) docstring piracy at load time.
The rendered check already handles this case — _requires_local_rendering returns false for a value that isa Module and is owned outside pkg's hierarchy, and the run_api_docs docstring says "Re-exported modules inherit the defining package's rendered module documentation." The docstrings check has no analogous exemption. That asymmetry looks unintended.
Reproduction
SciMLTesting v2.6.2, Julia 1.12.6. SciMLLogging (like SciMLBase) has no module docstring in any released version — src/SciMLLogging.jl line 1 is a bare module SciMLLogging.
using SciMLLogging: SciMLLogging
module Facade
using SciMLLogging: SciMLLogging
export SciMLLogging
end
println("hasdoc(Facade, :SciMLLogging) before = ", Base.Docs.hasdoc(Facade, :SciMLLogging))
println("Docs.Binding(Facade, :SciMLLogging) = ", Base.Docs.Binding(Facade, :SciMLLogging))
# What `@doc "..." SciMLLogging` inside the facade package would do.
Core.eval(Facade, :(Base.@doc "Re-export of `SciMLLogging`." SciMLLogging))
println("hasdoc(Facade, :SciMLLogging) after = ", Base.Docs.hasdoc(Facade, :SciMLLogging))
println("keys(Docs.meta(Facade)) = ", collect(keys(Base.Docs.meta(Facade))))
println("SciMLLogging.SciMLLogging in meta(SciMLLogging)? = ",
Base.Docs.Binding(SciMLLogging, :SciMLLogging) in keys(Base.Docs.meta(SciMLLogging)))
Output:
hasdoc(Facade, :SciMLLogging) before = false
Docs.Binding(Facade, :SciMLLogging) = SciMLLogging.SciMLLogging
hasdoc(Facade, :SciMLLogging) after = true
keys(Docs.meta(Facade)) = Any[]
SciMLLogging.SciMLLogging in meta(SciMLLogging)? = true
Docs.meta(Facade) stays empty and the entry lands in Docs.meta(SciMLLogging): the "local" docstring is a write into the dependency.
Where it bites
SciML/OrdinaryDiffEq.jl master. src/OrdinaryDiffEq.jl has export SciMLBase, SciMLLogging, remake, ..., so both module bindings are public API:
┌ Info: run_api_docs: public API names missing a docstring
│ pkg = OrdinaryDiffEq
│ undocumented =
│ 2-element Vector{Symbol}:
│ :SciMLBase
└ :SciMLLogging
ERROR: LoadError: Some tests did not pass: 87 passed, 1 failed, 0 errored, 0 broken.
public API has docstrings: Test Failed at ~/.julia/packages/SciMLTesting/v2jEQ/src/SciMLTesting.jl:1205
Expression: isempty(undocumented)
Evaluated: isempty([:SciMLBase, :SciMLLogging])
Its docs/make.jl works around it with load-time piracy that only runs during the docs build, which is why the Documentation lane is green while the QA lane is red:
@eval SciMLBase @doc "The common interfaces and types used throughout the SciML ecosystem." SciMLBase
@eval SciMLLogging @doc "The common logging interface used by SciML solvers." SciMLLogging
Proposed fix
In the docstrings branch of run_api_docs, skip a public name whose value isa Module and is not owned by pkg's module hierarchy — the same ownership test _requires_local_rendering already applies. A package's own submodules would keep their docstring obligation; only re-exported foreign modules would be exempt, matching the documented "a re-exported name documented in its defining package counts as documented" intent (there is simply nothing to follow when the defining package has no module docstring).
Orthogonally, SciML/SciMLBase.jl and SciML/SciMLLogging.jl should grow real module docstrings; that would make this particular instance green without any exemption, and would let OrdinaryDiffEq drop the two @eval lines from docs/make.jl. But it does not fix the general case, since a re-exporting package cannot force its dependency to be documented and cannot raise a compat floor for a docstring.
Meanwhile OrdinaryDiffEq is using the documented per-repo escape hatch api_docs_kwargs = (; ignore = (:SciMLBase, :SciMLLogging)), which should be removed once this is resolved.
Summary
run_api_docs's docstrings check requires a docstring for every name inpublic_api_names(pkg), including re-exported module bindings. A package that re-exports a dependency's module (export SciMLBase) cannot satisfy that check: Julia resolvesDocs.Binding(pkg, :SciMLBase)toSciMLBase.SciMLBase, so any@docwritten in the re-exporting package is stored in the dependency's doc registry, not its own. The only ways to make the check green are (a) a module docstring in the dependency, or (b) docstring piracy at load time.The rendered check already handles this case —
_requires_local_renderingreturnsfalsefor a value thatisa Moduleand is owned outsidepkg's hierarchy, and therun_api_docsdocstring says "Re-exported modules inherit the defining package's rendered module documentation." The docstrings check has no analogous exemption. That asymmetry looks unintended.Reproduction
SciMLTestingv2.6.2, Julia 1.12.6.SciMLLogging(likeSciMLBase) has no module docstring in any released version —src/SciMLLogging.jlline 1 is a baremodule SciMLLogging.Output:
Docs.meta(Facade)stays empty and the entry lands inDocs.meta(SciMLLogging): the "local" docstring is a write into the dependency.Where it bites
SciML/OrdinaryDiffEq.jlmaster.src/OrdinaryDiffEq.jlhasexport SciMLBase, SciMLLogging, remake, ..., so both module bindings are public API:Its
docs/make.jlworks around it with load-time piracy that only runs during the docs build, which is why the Documentation lane is green while the QA lane is red:Proposed fix
In the docstrings branch of
run_api_docs, skip a public name whose valueisa Moduleand is not owned bypkg's module hierarchy — the same ownership test_requires_local_renderingalready applies. A package's own submodules would keep their docstring obligation; only re-exported foreign modules would be exempt, matching the documented "a re-exported name documented in its defining package counts as documented" intent (there is simply nothing to follow when the defining package has no module docstring).Orthogonally,
SciML/SciMLBase.jlandSciML/SciMLLogging.jlshould grow real module docstrings; that would make this particular instance green without any exemption, and would let OrdinaryDiffEq drop the two@evallines fromdocs/make.jl. But it does not fix the general case, since a re-exporting package cannot force its dependency to be documented and cannot raise a compat floor for a docstring.Meanwhile OrdinaryDiffEq is using the documented per-repo escape hatch
api_docs_kwargs = (; ignore = (:SciMLBase, :SciMLLogging)), which should be removed once this is resolved.