Symptom
Every Documentation.yml run (CI, not just the new GPU pass — this predates #885) logs 7 warnings like:
┌ Warning: Failed to load inventory "CTBase" from possible source "/home/runner/work/OptimalControl.jl/OptimalControl.jl/docs/../../CTBase/docs/build/1/objects.inv".
│ exception = SystemError: opening file "...CTBase/docs/build/1/objects.inv": No such file or directory
└ @ DocumenterInterLinks ~/.julia/packages/DocumenterInterLinks/U7nII/src/interlinks.jl:233
One per control-toolbox sibling package (CTBase, CTDirect, CTFlows, CTLie, CTModels, CTParser, CTSolvers).
Why it happens
docs/make.jl:161-222 builds each InterLinks entry as an ordered tuple of sources, e.g.:
"CTBase" => (
"https://control-toolbox.org/CTBase.jl/stable/",
joinpath(@__DIR__, "..", "..", "CTBase", "docs", "build", "1", "objects.inv"),
"https://control-toolbox.org/CTBase.jl/stable/objects.inv",
),
DocumenterInterLinks tries sources in order and uses the first that loads. The local objects.inv path only exists for a contributor who has cloned the sibling repos side-by-side and built their docs locally — deliberately kept first so that cross-repo docstring/@extref changes can be verified locally before either side is released (this ordering was reaffirmed in the #885 follow-up discussion: local-first is wanted, not accidental). On the CI runner only OptimalControl.jl is checked out, so that path always fails, DocumenterInterLinks logs a Warning, then falls back to the remote URL, which succeeds. End result is correct; the log is just noisy — 7 warnings on every single run.
The other 4 entries (ADNLPModels, NLPModelsIpopt, ExaModels, MadNLP, Tutorials) use committed docs/inventories/*.toml files as their local source instead of a sibling build path, so they never hit this — only the 7 control-toolbox family entries are affected.
Requested fix
Keep local-first ordering for local dev, but only include the local sibling path when not running in CI, so the CI runner's InterLinks tuple has exactly 2 entries (base URL + remote inventory) and the local path — which can never succeed there — is never attempted:
# CI never has sibling repos checked out next to this one, so the local sibling
# `objects.inv` path can never resolve there — only include it outside CI, so
# DocumenterInterLinks doesn't log 7 "Failed to load inventory" warnings on every run.
# Order stays local-first when present: a contributor with siblings cloned and built
# locally sees their own in-progress docstring/@extref changes, not the last stable
# release.
const IN_CI = get(ENV, "CI", "false") == "true"
sibling_inventory(pkg, base_url) = IN_CI ? (base_url, "$(base_url)objects.inv") : (
base_url,
joinpath(@__DIR__, "..", "..", pkg, "docs", "build", "1", "objects.inv"),
"$(base_url)objects.inv",
)
links = InterLinks(
"CTBase" => sibling_inventory("CTBase", "https://control-toolbox.org/CTBase.jl/stable/"),
"CTDirect" => sibling_inventory("CTDirect", "https://control-toolbox.org/CTDirect.jl/stable/"),
"CTFlows" => sibling_inventory("CTFlows", "https://control-toolbox.org/CTFlows.jl/stable/"),
"CTLie" => sibling_inventory("CTLie", "https://control-toolbox.org/CTLie.jl/stable/"),
"CTModels" => sibling_inventory("CTModels", "https://control-toolbox.org/CTModels.jl/stable/"),
"CTParser" => sibling_inventory("CTParser", "https://control-toolbox.org/CTParser.jl/stable/"),
"CTSolvers" => sibling_inventory("CTSolvers", "https://control-toolbox.org/CTSolvers.jl/stable/"),
"ADNLPModels" => (...), # unchanged, all 4 below unchanged
...
)
Note: joinpath(pkg, "docs", "build", "1", ...) isn't uniform across the 7 — CTDirect.jl/CTFlows.jl/CTModels.jl/CTParser.jl carry the .jl suffix in their directory name (CTDirect.jl, not CTDirect) while CTBase/CTLie/CTSolvers don't; sibling_inventory's first argument must be the actual directory name, not the package name — check each existing joinpath(...) call in docs/make.jl when porting rather than assuming they all match the InterLinks key.
Non-goals
- Do not flip the ordering (URL-first): that was considered and rejected — it would make the CI-nominal path depend on network reachability of
control-toolbox.org instead of a deterministic local-path failure, to save only cosmetic log noise.
- Low priority / cosmetic only — no functional impact, log noise only.
Symptom
Every
Documentation.ymlrun (CI, not just the new GPU pass — this predates #885) logs 7 warnings like:One per control-toolbox sibling package (CTBase, CTDirect, CTFlows, CTLie, CTModels, CTParser, CTSolvers).
Why it happens
docs/make.jl:161-222builds eachInterLinksentry as an ordered tuple of sources, e.g.:DocumenterInterLinkstries sources in order and uses the first that loads. The localobjects.invpath only exists for a contributor who has cloned the sibling repos side-by-side and built their docs locally — deliberately kept first so that cross-repo docstring/@extrefchanges can be verified locally before either side is released (this ordering was reaffirmed in the #885 follow-up discussion: local-first is wanted, not accidental). On the CI runner onlyOptimalControl.jlis checked out, so that path always fails,DocumenterInterLinkslogs aWarning, then falls back to the remote URL, which succeeds. End result is correct; the log is just noisy — 7 warnings on every single run.The other 4 entries (
ADNLPModels,NLPModelsIpopt,ExaModels,MadNLP,Tutorials) use committeddocs/inventories/*.tomlfiles as their local source instead of a sibling build path, so they never hit this — only the 7 control-toolbox family entries are affected.Requested fix
Keep local-first ordering for local dev, but only include the local sibling path when not running in CI, so the CI runner's
InterLinkstuple has exactly 2 entries (base URL + remote inventory) and the local path — which can never succeed there — is never attempted:Note:
joinpath(pkg, "docs", "build", "1", ...)isn't uniform across the 7 — CTDirect.jl/CTFlows.jl/CTModels.jl/CTParser.jl carry the.jlsuffix in their directory name (CTDirect.jl, notCTDirect) while CTBase/CTLie/CTSolvers don't;sibling_inventory's first argument must be the actual directory name, not the package name — check each existingjoinpath(...)call indocs/make.jlwhen porting rather than assuming they all match the InterLinks key.Non-goals
control-toolbox.orginstead of a deterministic local-path failure, to save only cosmetic log noise.