Skip to content

[docs] reorganize into doc tabs - #1004

Merged
artberger merged 24 commits into
mainfrom
adb-doctabs
Sep 4, 2026
Merged

[docs] reorganize into doc tabs#1004
artberger merged 24 commits into
mainfrom
adb-doctabs

Conversation

@artberger

Copy link
Copy Markdown
Collaborator

Adopt new doc tabs Hugo theme feature to organize all versions and standalone/kubernetes sections into doc tabs: documentation, integrations, reference, release-notes. Includes updates to links, and reference docs gen workflow.

image

Adopt the docs-theme-extras `docTabs` layout so the left nav shows one
group at a time instead of the whole version tree. Four tabs, in order:
Documentation (default), Integrations, Reference, Release notes.

Each tab is a real directory under the version root, so this is a content
move as much as a config change:

- `documentation/` is new and takes every subject directory that used to
  sit at the version root (about, agent, llm, mcp, security, setup, …).
- `integrations/` and `reference/` already existed and stay where they are.
- `release-notes/` is new and takes `reference/release-notes.md` and
  `reference/versions.md` back out of the Reference tab.
- `not-in-version.md` deliberately stays at the version root: navbar.html
  and sidebar.html hardcode `<version>/not-in-version` for the
  version-switch fallback.

All ten published version roots move together, not just latest/main. Two
things force that. The theme enables the tab band wherever two or more of
the configured directories exist, and `integrations/` + `reference/`
already existed in 1.0.x–1.4.x and 2.2.x — so landing the config alone
would switch tabs on for versions with no `documentation/` directory to
root the nav in. And `assets/agw-docs/` is shared unversioned by every
version, so a half-migration would leave its ~218 files' link paths
correct for one group and wrong for the other.

Link surface updated in the same change: the `link`, `link-hextra`,
`card`, `badge`, and `redirect` shortcodes all resolve version-root-
relative paths, plus hardcoded URLs in layouts, `static/_worker.js`, the
Playwright fixtures, and the doc-test scripts' examples. `static/_redirects`
gains a rule per moved directory per version, and existing redirect targets
were repointed, so every previously published URL still resolves. Pages
with legacy `aliases` keep them and gain the moved-path form alongside.

Verified: `hugo --gc --minify` is clean, and an internal link check over
the built site finds the same 23 pre-existing dead links as origin/main —
no regressions, and the tab band plus per-tab nav scoping render on all
four tabs.

Signed-off-by: Art Berger <art.berger@solo.io>
The doc-test front matter names its source guides by repo-relative path, in
two forms the link rewrite did not cover:

  - file: content/docs/<mode>/<version>/<path>.md
  - file: ${versionRoot}/<path>.md          (expanded by doc_test_extract)

Both now carry the documentation/ segment. Without this the generator dies
on the first scenario with FileNotFoundError rather than failing a test, so
nothing runs at all.

Verified against a generation run of the same 458 scenarios on origin/main:
identical scenario count, identical selected_count and selected_test_count in
all 458 manifests, and 450 of 458 generated scripts byte-identical. The other
eight differ only in the `# Source:` line numbers, shifted by one because
those pages gained a companion alias line.

Signed-off-by: Art Berger <art.berger@solo.io>
Integrations pages that only pointed at a Documentation guide are now a
single entry that goes straight there, and the two tabs that had almost no
icons have a full set.

Standalone integrations:
- llm-providers/ was an index plus eight per-provider stubs, every one of
  them a `redirect` into documentation/llm/providers/. It collapses to one
  `llm-providers.md` that redirects to the section.
- mcp-servers/ collapses the same way to documentation/mcp/connect/. The
  three transport pages restated what the connect guides already cover; the
  one thing the landing had that the guides did not — the "why put
  agentgateway in front of an MCP server" table — moves into
  documentation/mcp/connect/_index.md rather than being dropped.

Kubernetes integrations:
- New LLM providers and MCP servers entries, pointing at
  documentation/llm/providers/ and documentation/mcp/. Every child of this
  section carries weight 10, so ordering is alphabetical by title and the two
  land directly after LLM clients and MCP clients.
- mcp-clients was a single page listing five IDEs. It is now a section with a
  page per client, matching how llm-clients is already organised: the shared
  setup (prerequisites, MCP endpoint URL, auth, next steps) stays on the
  landing, and each client gets its own page reusing a per-client asset. The
  two `mcp-clients-k8s` doc-test blocks live in the shared setup, so the
  scenario is unchanged — the regenerated script still carries both.

Release notes landing: an explicit card grid, including a Changelog card out
to the GitHub releases page. It also sets `disableCards: true` — the auto
child-card grid is only suppressed by that flag, not by the presence of manual
cards, despite what auto-section-cards.html's comment says.

Icons: every page in the Integrations, Reference, and Release notes tabs now
has one (234 added; 46/46, 25/25 and 26/26 coverage per tab). The rule follows
the Documentation tab — a brand logo from static/integrations/ where one
exists, otherwise the group's Material Symbol. The generated reference widget
pages carry theirs through the content adapters' params dict. Note that card
icons resolve differently from front-matter ones: card.html calls Hextra's
icon partial unguarded, so a card icon must be an SVG path or a data/icons.yaml
key — a Material Symbol name there aborts the build.

static/_redirects gains 52 rules so every collapsed integrations URL still
resolves, using the exact targets the deleted stubs redirected to.

Signed-off-by: Art Berger <art.berger@solo.io>
Ten of the icons added in the previous commit carried hardcoded brand colours,
so Helm, Argo, Istio, Claude and others rendered in blue/orange next to the
grey Material Symbols instead of matching them.

The repo already had the mechanism: colour logos live in static/integrations/
(the homepage grid uses them) and monochrome nav variants live in
static/integrations/providers/bw/, where every file is `fill="currentColor"` —
which is why the LLM provider pages in the Documentation tab always looked
right. This adds the missing variants and points the icons at them.

Eight convert by swapping the colour for currentColor, because none of them
paints a white knock-out layer: claude, istio (its two tones survive via
opacity), langchain, langfuse, aws, google-cloud, plus helm and argocd.

helm and argocd needed redrawing rather than substituting: both paint white
detail on top of a solid disc, so recolouring every fill gives a filled blob.
They are now line art — an outlined disc with the detail kept as a stroke,
which reads as a ship's wheel and a set of rings.

Two icons changed rather than converted:
- opencode's mark is two greys with no outline, and collapses to a filled
  square in a single colour. It falls back to /integrations/code.svg, which
  already inherits.
- kagent was pointing at the Kubernetes logo, which was simply wrong — kagent
  is not Kubernetes. It takes the web-uis group symbol instead.

Verified by rendering each new variant to PNG and looking at it, then scanning
the built site: 0 hardcoded colours across 74,515 inlined nav and card icons on
2,672 pages, with the dead-link set unchanged. Note that `rm -rf public` alone
is not enough to re-check this — the stale inlined SVGs survive in resources/,
so a verification build needs that removed (or --ignoreCache) or it reports
colours that are no longer in the source.

Signed-off-by: Art Berger <art.berger@solo.io>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Sep 1, 2026

Copy link
Copy Markdown

Deploying agentproxy with  Cloudflare Pages  Cloudflare Pages

Latest commit: 19d9519
Status: ✅  Deploy successful!
Preview URL: https://5e33f9ea.agentproxy.pages.dev
Branch Preview URL: https://adb-doctabs.agentproxy.pages.dev

View logs

@artberger artberger self-assigned this Sep 1, 2026
Signed-off-by: Kristin Brown <kristin.brown@solo.io>
Integrations was a flat list of nine-plus siblings. It now groups by subject:

  LLM/     providers, clients, routing (k8s), observability
  MCP/     servers, clients
  CI-CD/   argo
  + auth, cloud-providers, networking, web-uis, istio unchanged

Argo moves under CI/CD, and vLLM Semantic Router and KServe move under
LLM > Routing. Nested titles shed the now-redundant prefix ("LLM clients" ->
"Clients" under LLM), and weights put the primary child first: providers before
clients, servers before clients.

The provider and MCP-server guides change DIRECTION. They used to live in
Documentation with an Integrations stub redirecting to them; now the content
lives in Integrations and Documentation carries the pointer:

  integrations/llm/providers/   <- documentation/llm/providers/   (22 / 15 pages)
  integrations/mcp/servers/     <- documentation/mcp/connect/     (standalone)
  documentation/llm/providers   -> points at the Integrations page
  documentation/mcp/connect     -> points at the Integrations page

Kubernetes keeps its MCP-server guides (static-mcp, dynamic-mcp, virtual,
https, session) in Documentation — they are core guides, not integrations — so
its Integrations > MCP servers stays a pointer INTO Documentation. The two modes
differ here on purpose.

Icons are gone from the docs nav, so there is nothing to keep in sync as pages
move (692 removed, plus 11 hand-written card icons and 5 in the content
adapters). The only survivors are the LLM provider pages, whose brand logos also
drive the provider card grid. The three mode landings above the version roots
keep theirs — they belong to the /docs/ index, not to a version's nav.

Archived versions moved too, for the same reason the docTabs partition had to
cover them: assets/agw-docs/ is unversioned, so a link rewritten there resolves
against whichever version renders it. Leaving 1.0.x-2.2.x behind put 91 dead
links into them, which is how this was caught.

static/_redirects gains 258 rules. The two Documentation sections keep a real
page at the directory URL, so only their children need a rule; the renamed
groups get base, trailing-slash, and wildcard.

Verified: hugo builds clean, the dead-link set is byte-identical to origin/main
(23, all pre-existing), and doc-test generation still produces 458 scenarios
with their sources resolving to the new paths.

Signed-off-by: Art Berger <art.berger@solo.io>
…yout

main moved on while this branch was open — most substantially PR #990
("Observability updates for Kubernetes"), which restructured observability at
the PRE-docTabs paths, plus #998, #1006 and the timeout/agentcore edits.

Resolved by rule rather than by hand, because the two changes are orthogonal:
main's CONTENT wins for every file it touched, and this branch's LOCATION wins.
All 63 of main's content changes were re-applied at their docTabs path — for
these, `<version>/observability/…` becomes `<version>/documentation/observability/…`
— and this branch's transformations (the tab path prefix, the Integrations
regrouping, icon removal) were re-applied on top. Nothing of this branch's own
work is lost that way, because its edits to those files were only path rewrites
and icon removal.

The trap here is not the 30 conflicts, it is the files that merge CLEANLY. Most
of main's new observability pages had no counterpart on this branch, so git
happily added them at `<version>/observability/…` — a path that is outside every
tab in this layout, where they would render with no tab band and no nav. 35 such
duplicates were removed after their content was applied at the correct path.

Two files needed manual repair, both from git pairing a `latest` file of main's
against an ARCHIVED file of this branch by rename detection:
  - 1.3.x/documentation/observability/_index.md  (left conflict markers)
  - 1.3.x/documentation/observability/otel-stack.md  (mis-merged SILENTLY)
Both restored from this branch's side; main never touched 1.3.x. The silent one
was caught by diffing every archived version root against the pre-merge commit,
which is now the check to run after any merge into this branch — archived roots
must come out byte-identical.

Note the conflict markers were eight brackets deep, not seven, so a `^<<<<<<< `
grep misses them.

Verified: hugo builds clean, and an internal link check finds the same 44 dead
links as origin/main built at the same commit — no regressions. (Main itself went
from 23 to 44; the 21 new ones arrived with #990/#1006 and are not addressed
here.)

Signed-off-by: Art Berger <art.berger@solo.io>
Signed-off-by: Kristin Brown <kristin.brown@solo.io>
Cloudflare allows 2,000 static and 100 DYNAMIC redirects per _redirects file,
and any rule carrying a splat or a :placeholder is dynamic. Writing the doc-tab
moves out per version had reached 278 dynamic rules — nearly three times the
ceiling, which would have broken the deploy rather than degrading quietly.

Collapsed to 51. `:section` and `:version` each match one segment, so a single
rule covers kubernetes and standalone across every published version, and a
trailing `*` covers the bare path, the trailing-slash form, and everything
beneath it at once. Static rules are untouched at 92.

Order is load-bearing, since Cloudflare applies the first rule that matches:
the paths that left Documentation entirely (llm/providers, mcp/connect) precede
the plain llm*/mcp* rules that would otherwise swallow them, and
vllm-semantic-router precedes vllm.

Three cases the compact form does not cover on its own, all verified rather
than assumed:
  - llm/inference and llm/multiple-inference-pools set an explicit `url:`, so
    they do not publish where their file path implies. A parent's `url:` does
    NOT move its children, so inference-routing keeps the plain documentation/
    prefix while the landing goes to documentation/inference.
  - integrations/llm-providers and integrations/mcp-servers were real published
    stub pages, and two of their leaves were renamed on the way (azure-openai ->
    azure, openai-compatible -> custom, streamable-http -> http).

Checked by replaying the rule file the way Cloudflare does — first match wins,
:placeholder is one segment, * is greedy — over every URL origin/main publishes:
2,584 URLs, 304 still served directly, 2,280 covered by a redirect, 0 uncovered.
Pages carrying a `url:` override are excluded, since they never published at
their file path in the first place.

Signed-off-by: Art Berger <art.berger@solo.io>
artberger and others added 15 commits September 1, 2026 14:00
Discover test cases — the "Get changed markdown files" step trimmed its file
list with `xargs`, which re-wraps output at about 4,096 characters. On a PR this
size that turned a single-line `files=...` into a multi-line value and the step
died with "Unable to process file command 'output' successfully". Trim with sed
instead, and write through the heredoc form so length can never break the
format again. Not specific to this PR: any change touching a few hundred pages
would have hit it.

Static + content checks — otel-stack.md used the deprecated
`{{< tabs items="..." >}}` form with `tabName=` on each tab, which
tab-syntax.spec.ts rejects. Converted to per-tab `name=`. This arrived with the
observability rework rather than from this branch (origin/main carries the same
two lines), but it fails here, so it is fixed here.

Signed-off-by: Art Berger <art.berger@solo.io>
Signed-off-by: Kristin Brown <kristin.brown@solo.io>

# Conflicts:
#	assets/agw-docs/pages/security/access-logging.md
Two defects from merging main into the docTabs layout.

Rename detection paired main's standalone/main/.../access-logs/view.md
with the docTabs standalone/LATEST file, because the two version trees
are near-identical. The guardrails section from #1005 landed in latest
and never reached main. Move it to the tree #1005 targeted.

Also prefix the guardrails cross-link with documentation/, since llm/
lives under the documentation tab now.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Kristin Brown <kristin.brown@solo.io>
The restructure in #990 deleted latest/security/access-logging and
main+latest/observability/tracing, but other pages still link to them.
Add stubs at the docTabs paths, matching the one #990 already left at
main/documentation/security/access-logging.

The tracing stubs set sidebar.exclude so a "Tracing" entry does not sit
beside the new "Traces" section.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Kristin Brown <kristin.brown@solo.io>
"Discover test cases" has failed on every run of this branch. The list of
changed markdown files reached the "List test cases" step as an env var,
and on a PR this size it is ~263 KB. Linux caps a single environment
string at MAX_ARG_STRLEN (128 KiB), so execve failed with "Argument list
too long" before that step's bash started -- the annotation names bash,
not the python script, which runs fine on the same input.

This is the same oversized list that broke the previous step in 16955aa;
fixing the write moved the failure one step downstream to the read.

Write the list to $RUNNER_TEMP/changed-files.txt, one path per line, and
read it back into an array. Each path is then its own short argv entry, so
only the total matters (263 KB against a ~2 MB ARG_MAX), and no single
string approaches the per-string cap.

Verified locally under bash: 3909 paths in, 1038 test cases discovered,
40 shards, largest per-shard SHARD_TESTS 3069 bytes. The no-file fallback
still selects full discovery for scheduled and dispatch runs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Kristin Brown <kristin.brown@solo.io>
The kubespec widgets carry ~3300 "Documentation References" links of the
form ../../../llm/rbac/. From <version>/reference/api-kubespec/<kind>/
that resolves to <version>/llm/rbac/, which docTabs moved to
<version>/documentation/llm/rbac/. This was the bulk of the link
checker's 275 errors.

render.go needs no change. It builds each href as "../../../" + the path
of the page that referenced the schema field, and extract_rendered_yaml.py
derives those paths from the built site -- run against this branch it
already emits documentation/... keys. The committed HTML is simply stale,
generated from a pre-docTabs build. This commit brings it in line with
what the next reference-docs run produces, so that run is a no-op rather
than churn.

Each href was remapped with the wildcard rules in static/_redirects, in
Cloudflare's first-match order, then verified against the built site:
3322 rewritten, 18 already correct, 0 unresolved. Only ks-doc-link hrefs
differ -- 1291 changed line pairs, none differing outside the href.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Kristin Brown <kristin.brown@solo.io>
Blog posts and a few doc pages still linked to pre-docTabs URLs.

Blogs: 38 links across 11 posts, remapped with the static/_redirects
wildcard rules and verified against the built site. The redirects would
have caught these at runtime, but only as 301s, and agentgateway.dev
applies a limited number of rules.

Doc pages: four relative links that survived the move because they are
relative and so kept resolving to a plausible-looking wrong path --
../../api-types and ../api-types from the standalone LLM provider pages,
and ../connect/stdio from mcp-observability. Converted to link-hextra
with absolute paths so a future section move breaks the build loudly
instead of silently retargeting them.

Local lychee run over the built site, with the same config and flags CI
uses: 463818 links checked, 0 errors, down from 275.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Kristin Brown <kristin.brown@solo.io>
Signed-off-by: Nadine2016 <nadine.spies@solo.io>
Signed-off-by: Nadine2016 <nadine.spies@solo.io>
Signed-off-by: Kristin Brown <kristin.brown@solo.io>
Signed-off-by: Kristin Brown <kristin.brown@solo.io>
Signed-off-by: Kristin Brown <kristin.brown@solo.io>
Carries main's content into the docTabs layout. Git's rename detection
mis-routed several changes between version trees, because every version
root was partitioned into the same documentation/integrations/reference
tabs and the trees look alike to similarity matching. Corrected:

- backend.md landed in 1.2.x and 1.3.x; main added it to latest and main.
- The "Backend policy fields" section landed in 1.2.x and 1.3.x; main
  added it to latest and main.
- The access-log OpenTelemetry preset section landed in standalone/latest;
  main added it to standalone/main.
- The security/access-logging aliases were swapped between latest and main,
  so each tree aliased the other's path.

Also in this merge:

- Release notes for both modes take main's version wholesale. Main reset
  the `main` tree to a fresh 1.6.x cycle, and the branch's only change to
  that file was link repointing.
- The three conflicted generated API reference files take main's
  regenerated output, with the ks-doc-link hrefs repointed at the docTabs
  paths. Verified byte-identical to main apart from hrefs.
- 39 hand-written link-hextra and card paths that main added are repointed
  at their docTabs tab.
- after-title.html and the agentgateway-crds..md typo duplicate are
  dropped, both deleted in main. The branch's only edit to after-title
  was a comment, and main replaced the slot with under-heading.html.
- filter-order.md takes main's weight: 5 reordering.

KNOWN, INHERITED FROM MAIN: main added two links to shared asset bodies
without a version gate that covers the frozen trees, so 1.0.x, 1.1.x,
1.2.x, 1.3.x, 1.4.x, and 2.2.x each link a page that exists only in
latest and main. 18 broken in-body links total. These break identically
on main and are not a merge regression.

Signed-off-by: Kristin Brown <kristin.brown@solo.io>
`backend.tcp.connectTimeout` and `backend.http.requestTimeout`, and the
`resiliency/timeouts/backend` page that documents them, arrived in 1.5.
Both shared assets picked up the new content ungated, so 1.0.x-1.4.x and
2.2.x described a feature they do not have and linked to a page that does
not exist in them — 6 dead links, 12 occurrences.

`exclude-if` rather than `include-if` so a new version needs no edit here.
Note the list is not what covers the archived versions: hugo.yaml
params.versions registers only 1.5.x and 1.6.x, and a version gate drops
its content wholesale in an unregistered version directory. That is why
2.2.x is correctly excluded without being named. The list states the
intent and takes effect for any version that is registered.

The table rows carry their markers at the START and END of the row line,
not on lines of their own: a marker on its own line inside a table
truncates the table at that point.

Verified against rendered output: 4 table rows in every archived version
and 6 in latest and main, with the request and per-try rows intact
everywhere; the webhook-timeout section absent in archived versions with
Step 3 still following it. Dead internal links go from 52 to 46 (89 to 77
occurrences) with none newly broken, which puts the branch at 0 links
introduced and 3 removed against its state before the main merge.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: Art Berger <art.berger@solo.io>
Signed-off-by: Nadine2016 <nadine.spies@solo.io>
@artberger
artberger merged commit a9d1e2a into main Sep 4, 2026
47 checks passed
@artberger
artberger deleted the adb-doctabs branch September 4, 2026 18:49
@artberger artberger mentioned this pull request Sep 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants