Skip to content

Mount a declared API without a toc entry - #4738

Merged
Mpdreamz merged 26 commits into
mainfrom
feature/floating-implicit
Nov 5, 2026
Merged

Mpdreamz merged 26 commits into
mainfrom
feature/floating-implicit

Conversation

@Mpdreamz

@Mpdreamz Mpdreamz commented Nov 5, 2026

Copy link
Copy Markdown
Member

A docset that declares an API in its api: block now has that API in its navigation tree, in isolated and assembler builds, with no toc entry. Its links.json lists the API, so cross-links to it can resolve. The explicit - api: <key> toc entry is removed.

Affects: Navigation, Links & redirects, Assembler builds

Prompt summary: Make API pages part of the navigation tree in isolated and assembler builds, so the sitemap, links.json and llms.txt can see them. A docset that defines an API section has it implicitly, with no toc entry, and the way an isolated build shows it in its sidebar is decided later. This is the fifth step of the design in RFC: Floating sections in the navigation tree.

Why

The earlier steps mount an API only where a toc says - api: <key>, and only the isolated build can. No docset has such an entry, so no links.json has a floating section and no cross-link to an API can resolve. The api: block already declares everything the build needs.

What

Implicit mount

A provider can opt in with MountsImplicitly. DocumentationSetNavigation then mounts every section the provider offers, as an island after the toc entries. The API provider opts in, so each declared API is in the tree. The mounted roots are hidden in the sidebar of the docset, so no sidebar changes. How an isolated build shows its APIs is a later decision.

The toc entry is gone

FloatingSectionRef, its parser and its resolution are removed, along with the lookup of a section by key. A toc with - api: <key> now reports Unknown key 'api'.

Assembler docsets

Each assembler docset gets its own API provider when the build renders the API Explorer. AssemblerApiSections now creates the generators before the docsets resolve their trees, so each docset fills its sections from the same trees the OpenAPI step renders from, and nothing is fetched twice. The APIs section of the site navigation lists the same APIs and keeps its visible roots.

Docs

docs/data/openapi/api-explorer.md describes the implicit mount, and docs/documentation/assembler/navigation.md says the docset needs no entry.

Stack: 5, on top of #4737.

Out of scope: showing an API in the sidebar of an isolated build, the serve and Codex builds, and replacing the link check in #4653. The api: toc entry had no users and is not kept as an override.

Verify

mise run test
dotnet test --project tests/Navigation.Tests --treenode-filter "/*/*/FloatingSectionNavigationTests/*"
dotnet test --project tests/Elastic.Documentation.Build.Tests --treenode-filter "/*/*/AssemblerOpenApiBuildStepIntegrationTests/*"

The integration test needs network access to CloudFront. It checks that an assembled docset publishes its API in links.json with no toc entry.

To see it in an isolated build, build this repository's docs and read links.json:

dotnet run --project src/tooling/docs-builder -- --path docs --output .artifacts/api-implicit-check

It lists all ten declared APIs under floating, and the docs sidebar does not change.

🤖 Generated with Claude Code

Mpdreamz and others added 20 commits November 5, 2026 12:02
A toc entry such as `api: elasticsearch` now mounts a floating section: a set of pages that owns its URL root and sidebar and is not made of Markdown files. A provider resolves the section before navigation is built, and it renders as an island. `CreateLinkReference()` skips these nodes instead of casting them to `MarkdownFile`. No provider is registered yet, so a build that uses the entry reports an error.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…entries

The site assembler dropped every root child of a mounted docset and warned
that it was undeclared, so an `api:` entry vanished from the assembled
navigation. Floating section roots now carry a marker interface and the
assembler keeps them. An `api:` entry that also sets `children:`, `file:` or
other keys now reports an error instead of silently dropping them.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A floating section root now exists as soon as the toc mounts it, with its landing URL and title, so layout code can link to it. The children come later from `ResolveAsync`, which `DocumentationSet.ResolveFloatingSectionsAsync` runs before generation. The children keep their own numbering, so filling them never renumbers the surrounding navigation.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The narrative root copied only leaf items into the site tree, so an
`- api: <key>` entry in its toc resolved and then vanished. The root now
keeps floating section roots next to its leaves.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
ResolveDirectoryTree now fills floating sections, so DocumentationGenerator, the assembler and the codex build see their pages without a separate call. The test goes through ResolveDirectoryTree and checks resolution runs once.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
An `api: <key>` toc entry now resolves to an API section. Its root exists when the toc is read, and `ResolveAsync` fills it with the version trees before generation. The generator builds each API's trees once and renders from them, so mounting a section adds no second fetch or build. Only the current version is visible.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The docset now fills floating sections itself, so a build run with `--skip-api` resolves an `api:` entry without a generator. The section stays empty and hidden instead of failing.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ip-api

A spec that fails to build no longer stops the Markdown build: the section reports the error and hides. The section also hides when no main version resolved, and a hidden API no longer appears in the section dropdown. The generation state records --skip-api, so toggling it triggers a full build. The docs now say that only the isolated build accepts api: entries.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The floating section no longer emits its own error when tree building fails, because the generator awaits the same cached task and already reports it with the stack trace. The no-main test now makes main unreadable without throwing, so it proves released versions loaded. A new build-twice test covers toggling --skip-api over unchanged pages.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A mounted API now publishes its pages in `links.json` under `floating`, one entry per page path with a bit mask of the versions that contain it. A cross-link such as `elasticsearch://api/elasticsearch/v8/operation/operation-search` resolves through hash lookups and a bit test, and a page missing in that version is an error. The resolver builds the final URL from the site root, because API pages sit outside every repository path prefix.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
An alias now belongs to the versions that list it, so a link to an alias
in a version without it fails, and an alias can name different pages in
different versions. A cross-link to an API page keeps its #fragment.
CodexAwareUriResolver sends API pages to the published site instead of
a repository path.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A fragment stays on the resolved URL and the build does not check the anchor. Aliases of a collapsed operation resolve only in the versions that list them.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A floating link section no longer assumes how a kind routes its URLs. Each version names its root, and whether it is hidden or current, so a reader joins a root and a page path and never adds a `v` segment. A cross-link picks a version by its name, and aliases are listed under their canonical path, which writes that path once however many aliases it has.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
An API page called `main` or `8` was read as a version, so the link reached
the landing page. A page of the current version now takes precedence, so
adding a version never changes where an existing link points. The current
version's bit is cached instead of scanned for on every unversioned link.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The lookups of a section are built on first use. The copy that a `with` expression makes carried them over, so replacing the versions or the aliases kept answering from the old data, and a test failed only when another test had used the shared fixture first. A copy now keeps the data and none of the lookups.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A `section: APIs` with `floating: api` in navigation.yml now mounts every declared API under a hub, so the sitemap walk and the llms.txt listing read the APIs from the tree like any other page. The roots exist when the navigation is built and the OpenAPI step fills them from the trees it renders from. GenerateApiSection and its catalog side door are gone, and the walk skips links to a heading inside a page.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ML exports

The hub URL has no trailing slash, so the sitemap filter now matches it exactly.
The top navigation tab for a floating section without external: links the hub
instead of the first child. API sections are only mounted when the HTML export,
and with it the API build step, runs.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The exporter guard sat inline in the build service, so no test reached it. It now lives in AssemblerApiSections.For, and a test checks that an LLM-only build gets no API sections while an HTML build keeps them.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A docset that declares an API in its api: block now has that API in its navigation tree, in isolated and assembler builds, hidden in the docset sidebar. The explicit api: toc entry is gone, so its links.json lists the API without anyone editing a toc. Each assembler docset resolves its sections from generators that the build creates first, and the site APIs section lists the same APIs.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@Mpdreamz
Mpdreamz requested a review from a team as a code owner November 5, 2026 14:43
@Mpdreamz
Mpdreamz requested a review from technige November 5, 2026 14:43
AssembleAsync resolves every docset tree before the API generators exist, so the API sections stayed empty and links.json had no floating entries. The build now resolves the floating sections again once the generators are attached, and a section starts from how its root was created.

The section dropdown no longer lists hidden top-level roots, such as implicitly mounted APIs.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…erators

The build resolves the docset trees before any generator exists, then attaches the generators and resolves the floating sections again. The new test follows that order. It checks that the root fills, stays hidden and publishes its pages in links.json. Without the second resolution the test fails.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Mpdreamz and others added 2 commits November 5, 2026 16:58
The APIs hub and the root of each API take a place in the global navigation order, so the pager walked into them. In the preview navigation the Reference page had the Logstash API as its previous page, and the last page of the docs had the APIs as its next page. The previous and next links now skip the hub and every floating root.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Base automatically changed from feature/floating-assembler to main November 5, 2026 20:01
@Mpdreamz
Mpdreamz changed the base branch from feature/floating-assembler to main November 5, 2026 20:01
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@Mpdreamz
Mpdreamz merged commit 1773748 into main Nov 5, 2026
36 checks passed
@Mpdreamz
Mpdreamz deleted the feature/floating-implicit branch November 5, 2026 20:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant