docs: make five example blocks show what they claim - #877
Merged
Conversation
The first CI build confirmed what a local build had suggested: several pages render, publish, and are wrong, without failing the build. Each fix below was checked by running the code in the docs environment, not by reading it. flows/overview.md — the "no integrator loaded" demonstration could not fail. `docs/make.jl` loads OrdinaryDiffEq for the whole site and a Julia extension stays armed for the session, so the `try` block succeeded and the page rendered a fully constructed OptimalControlFlow where it promised an ExtensionError. No page in this build can demonstrate it, so the block becomes an inert transcript captured in a session that loads OptimalControl and NLPModelsIpopt and nothing else, plus a note explaining why it is inert so it is not turned back into an `@example` later. The claim itself holds: `Flow(ocp, law)` throws `CTBase.Exceptions.ExtensionError` at construction time, naming the `using` to add. results/plot.md — the page said a flow call "only accepts variable/unsafe (and augment, for costate augmentation)". `augment` does not exist; CTFlows.jl/src/Flows/calling.jl:92 declares variable, unsafe, variable_costate. The migration page says so twice, and `augment=` is on the banned-spelling list in the cahier des charges §8.2. flows/accessors.md — eight lines of `import CTFlows.Systems:` with `# hide` on every one, needed before #868 re-exported all seven accessors. The page was quietly working around a re-export it should have been demonstrating. Removed; the built page now returns 0.5 from `hamiltonian(f)` with no import at all. migration.md — `LiftedHamiltonianFunction` was spelled two ways eighty-eight lines apart on the same page. Line 51 now matches line 139, the four geometry pages, and the test suite. The transcript further down keeps `CTLie.` because that is verbatim what src/deprecated.jl prints. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The page defined no `ocp` anywhere, and both demonstrations wrap their call in
try/catch, so the missing binding was swallowed and published as the result.
The rendered page read "…so this raises AmbiguousDescription rather than
silently picking one:" followed by UndefVarError(:ocp, …), and again eighty
lines down under "confirmed live".
Give the page a running example. It is a visible `@example`, not a hidden
`@setup`: a hidden binding would keep the page from being copy-pasted, which
moves the UndefVarError from the built page to the reader's REPL rather than
removing it. The dynamics are written coordinatewise so both the `:adnlp` and
the `:exa` modeler accept the problem.
With the page executing, both outputs could be read for the first time, and the
scheme table turned out to be wrong on four rows rather than one. Sweeping all
ten schemes against both modelers, each cell obtained by actually solving:
- `:exa` accepts only :trapeze, :midpoint, :euler and :euler_implicit.
The aliases are rejected where their canonical form is accepted —
:euler_forward fails where :euler succeeds, :euler_backward where
:euler_implicit succeeds — though under :adnlp the two name one scheme.
The table presented them as interchangeable.
- `:variable` is rejected under both modelers. It dispatches to
CTDirect.VariableStepODE, whose implementation is not compiled in
(CTDirect.jl/src/CTDirect.jl:63 has the include commented out), so a solve
raises UndefVarError rather than any typed error.
The table becomes a scheme × modeler matrix. `:variable` stays in it, marked
unavailable on both sides, because `describe(:collocation)` runs ten lines above
on the same page and still advertises it — removing the row would leave two
adjacent blocks contradicting each other.
The Gauss-Legendre demonstration silences ExaModels' `ExaCore()` deprecation
warning, visibly and with a comment. This page is the first in the site to run
the `:exa` modeler live — solve/gpu.md is Draft = true and solve/options.md's
:exa blocks are inert — so without it the published page would carry a warning
and the build machine's package path.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This was referenced Aug 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The first CI build confirmed what a local build had suggested: five pages render, publish, and
are wrong. None of them fails the build — that is the point. Every fix here was checked by
running the code in the
docs/environment, not by reading it, and two of them turned outdifferently from what the review predicted.
The five
1.
solve/choosing-a-method.mdpublished anUndefVarError. Noocpwas defined anywhereon the page, and both demonstrations wrap their call in
try/catch, so the missing binding wasswallowed and printed as the result:
and again eighty lines down under "confirmed live".
The page now has a running example — a double integrator, dynamics written coordinatewise so
both
:adnlpand:exaaccept it. It is a visible@example, not the hidden@setupthereview first proposed: a hidden binding keeps the page from being copy-pasted, which moves the
UndefVarErrorfrom the built page to the reader's REPL instead of removing it.2.
flows/overview.md's "no integrator loaded" demonstration could not fail.make.jlloadsOrdinaryDiffEqfor the whole site, and a Julia extension stays armed for the session, so thetryblock succeeded and the page rendered a fully constructedOptimalControlFlowwhere itpromised an
ExtensionError.No page in this build can demonstrate it, so the block becomes an inert transcript, captured in a
session loading
OptimalControlandNLPModelsIpoptand nothing else — plus a note explainingwhy it is inert, so it is not turned back into an
@examplelater. The claim itself holds:Flow(ocp, law)throwsCTBase.Exceptions.ExtensionErrorat construction time, withHint Run: using OrdinaryDiffEqTsit5.3.
results/plot.mdcontradicted the migration page. It said a flow call "only acceptsvariable/unsafe(andaugment, for costate augmentation)".augmentdoes not exist:CTFlows.jl/src/Flows/calling.jl:92declaresvariable,unsafe,variable_costate. Themigration page says so twice (
:81,:243), andaugment=is on the banned-spelling list in§8.2.
4.
flows/accessors.mdcarried a hidden import. Eight lines ofimport CTFlows.Systems:with# hideon every one — needed before #868 re-exported all seven accessors(
src/imports/ctflows.jl:20-27, with a testset). The page was quietly working around a re-exportit should have been demonstrating. Removed; the built page now returns
0.5fromhamiltonian(f)with no import at all.5.
migration.mdspelled one type two ways, on the same page.:51wroteCTLie.LiftedHamiltonianFunction,:139wroteOptimalControl.LiftedHamiltonianFunction. Thefour
geometry/pages and the test suite (test/suite/reexport/test_ctlie.jl:96-108) use thesecond form, and the same table two rows down states the rule — write against
OptimalControl.The transcript at
:196keepsCTLie.because that is verbatim whatsrc/deprecated.jl:63prints.
What re-reading the outputs found
With the page executing for the first time, the scheme table turned out to be wrong on four
rows, not one. Sweeping all ten schemes against both modelers, each cell obtained by actually
solving:
:adnlp:exa:trapeze,:midpoint,:euler,:euler_implicit:euler_explicit,:euler_forward,:euler_backward:gauss_legendre_2,:gauss_legendre_3:variableTwo surprises. The aliases are rejected under
:exawhere their canonical form is accepted —:euler_forwardfails where:eulersucceeds — though under:adnlpthe two name one scheme;the table presented them as interchangeable. And
:variableis rejected everywhere: itdispatches to
CTDirect.VariableStepODE(CTDirect.jl/src/DOCP_data.jl:338-340) whoseimplementation is not compiled in —
CTDirect.jl/src/CTDirect.jl:63has theincludecommentedout — so a solve raises
UndefVarError(:VariableStepODE, …, CTDirect).:variablestays in the table, marked unavailable on both sides, becausedescribe(:collocation)runs ten lines above on the same page and still advertises it. Removingthe row would leave two adjacent blocks contradicting each other. It comes out when CTDirect is
fixed.
Upstream, not fixed here
CTDirect—:variableadvertised by the registry, not compiled in,UndefVarError.The most serious of the three: it reaches anyone who follows the docs.
CTParser— an unknown scheme is thrown as a bareString(
onepass.jl:941,:1035,:1106).typeof(e) === String, outside the seven typedexceptions, and a
catchcannot discriminate on it.CTParser—onepass.jl:1483calls the deprecatedExaCore(). This page is the first inthe site to run
:exalive (solve/gpu.mdisDraft = true,solve/options.md's:exablocks are inert), so without intervention the published page would carry a deprecation warning
and the build machine's package path —
/home/runner/.julia/packages/ExaModels/…in CI. Itis silenced in the block visibly, with a comment naming the cause; hiding it behind
# hidewould have been the same mistake as fix 4 above. The scaffolding comes out whenCTParser is fixed.
Not done here
The site uses
println(e)30 times and the Handbook's canonical exception form(
VITEPRESS-DOC.md:241-260—@repl+try/catch # hide+showerror(IOContext(stdout, :color => false), e) # hide) zero times, where CTBase applies it24/24, CTSolvers 13/13 and CTModels 10/10. That is the same theme as this PR but thirty more
blocks, so it goes in its own change.
Verification
julia --project=docs docs/make.jllocally: 786 log lines, 12 errors, 186 warnings, 166unresolved
@ref, zero failing@exampleblock, exit 0 — identical to the baseline from #875.The 166 are the generated API reference and are untouched here. Both corrected outputs were read
in the built HTML, not only counted in the log.
🤖 Generated with Claude Code