| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
Zensical discovers pages with an unsorted directory walk and renders them all through one shared mkdocstrings handler, so the griffe collection that resolves cross-package re-exports (mcp -> mcp_types) accumulates in filesystem-dependent order. A GitHub runner-image update reshuffled readdir order and every CI docs build started failing with AliasResolutionError. The per-page preload_modules hint on the mcp index page could not help: mkdocstrings-python silently ignores it once the page's root package is already collected, which any earlier `::: mcp.<submodule>` page guarantees. Chase exported aliases at load time instead (load_external_modules: true), drop the dead per-page preload mechanism, and add a gauntlet step that renders the API pages in worst-case orders so an order-dependence regression fails on every machine instead of only on unlucky filesystems.
📚 Documentation preview
|
Sorry, something went wrong.
There was a problem hiding this comment.
LGTM — docs build tooling only, no runtime or public API changes.
What was reviewed:
This PR touches only docs build tooling: a one-option mkdocstrings config change in mkdocs.yml (load_external_modules: true), removal of the per-page preload_modules emission from scripts/docs/gen_ref_pages.py (with a docstring update explaining the new invariant), a new ~110-line CI gauntlet script scripts/docs/check_render_order.py wired into scripts/docs/build.sh, a private→public rename in scripts/docs/llms_txt.py so the new script can reuse the page-URL mapping, and a CI workflow comment update. No library code under src/ is modified.
None. The change is confined to the documentation build pipeline; it does not touch auth, crypto, permissions, network-facing code, or anything that ships in the published package. The new script only reads files under docs/api/ produced by the repo's own generator and renders them locally.
Low-to-moderate. This is build tooling, not production code, and the entire change is exercised end-to-end by the checks / docs CI job on this very PR — the new check runs on every build, so a mistake in it fails loudly rather than silently. The PR fixes a real, well-diagnosed CI breakage (order-dependent alias resolution that flipped when a runner-image update reshuffled readdir order), and the description documents both a positive reproduction and a negative test of the new check. The riskiest aspect is that check_render_order.py imports Zensical internals (zensical.compat.mkdocstrings, zensical.markdown.render), but the toolchain is lock-pinned and any upstream break would surface as a loud CI failure, not a silent regression.
I verified the _page_url → page_url rename left no stale references, that no preload_modules references remain anywhere, and that the sibling-import pattern and top-level except Exception handler in the new script follow the repo's established conventions (build_config.py uses the same import pattern; AGENTS.md explicitly permits top-level handlers). The bug hunting system found no issues. The PR timeline has no outstanding reviewer comments. Worst-case blast radius is a docs build failure, which CI catches immediately.
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
Every checks / docs job on main and on PRs has been failing since 2026-07-15/16 with AliasResolutionError: Could not resolve alias mcp.ClientResult pointing at mcp_types.ClientResult. This makes the docs build order-independent so it cannot recur, and adds a gauntlet check that proves it.
Motivation and Context
No repo change caused the failures. The docs build has been order-dependent since the Zensical switch (#3073): Zensical discovers pages with an unsorted directory walk and renders them all through one shared mkdocstrings handler, so the griffe collection that resolves ::: module blocks accumulates in filesystem-dependent order. A cross-package re-export (mcp → mcp_types) resolves only if some mcp_types page happened to be collected before docs/api/mcp/index.md renders.
The GitHub runner image update 20260705.232.1 → 20260714.240.1 (rolled out Jul 15–16) reshuffled readdir order on the runners and flipped every job from the lucky order to the unlucky one — perfect correlation across 9 jobs, including a fail/pass pair 48 minutes apart on the same branch (fail on the new image, pass on the old), and #3095's own docs check passing on essentially the same tree that then failed on main.
The existing preload_modules: [mcp_types] hint on the generated mcp index page didn't help: mkdocstrings-python only honors preload_modules when the page's root package is not yet collected, and any ::: mcp.<submodule> page collected earlier loads the whole mcp package, silently disarming the hint. It was already dead on machines where the build passed — those passed purely because mcp_types pages happened to render first.
Sorting the walk wouldn't fix this — sorted order (mcp/cli/… < mcp/index.md, mcp_types last) is itself a failing order. The correct invariant is order-independence:
How Has This Been Tested?
With the exact locked toolchain (zensical 0.0.50, mkdocstrings 1.0.4, mkdocstrings-python 2.0.5, griffelib 2.1.0) on CPython 3.12:
Replaying CI's failing page order without the fix reproduces the exact CI error; with the fix it passes, as do shuffled orders.
zensical build --strict: No issues found, ~16s (unchanged).
check_render_order.py: all four passes green ('mcp' index-last, 'mcp' index-alone, 'mcp_types' index-last, 'mcp_types' index-alone).
Negative test — removing load_external_modules from the generated config makes the new check exit 1 with the original error and a pointer to the fix:
The docs job gains roughly one extra build's worth of rendering (each API page re-rendered once, plus each package index once more).
Breaking Changes
None — build tooling only; no public API or published docs content changes.
Types of changes
Checklist
Additional context
Upstream reports worth filing separately: Zensical's unsorted page walk makes builds environment-dependent; mkdocstrings-python silently ignores preload_modules when the root is already collected; the flat (group_by_category: false) children template resolves aliases unguarded, turning an unresolved alias into a build crash instead of a skipped member.
AI Disclaimer