Skip to content

feat(reference) Move each port's reference under its own version - #16

Merged
tony merged 17 commits into
mainfrom
api-route-fix
Sep 13, 2026
Merged

tony merged 17 commits into
mainfrom
api-route-fix

Conversation

@tony

@tony tony commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

/en/reference/go/ put the Go API outside the Go documentation, above the version it documents, and mixed three separately versioned packages into one tree. Each reference now lives under the port and version it documents, and the Workspace Manager and the MCP server have references of their own.

Before After
core library /en/reference/go/<symbol>/ /en/go/latest/reference/<symbol>/
Workspace Manager /en/go/latest/workspace/internals/api/<symbol>/ /en/go/latest/workspace/reference/<symbol>/
MCP server /en/go/latest/mcp/api/<symbol>/ /en/go/latest/mcp/reference/<symbol>/
cross-port hub /en/reference/ unchanged
A–Z symbol index /en/reference/symbols/<letter>/ unchanged

How it works

  • referenceUrl(port, version, product) and referenceSegment() are the only place a reference path is spelled.
  • The reference route renders inside each port's shell, where Astro's base is already /<port>/<version>/. The core tree holds core symbols alone, so the 1,732 duplicate product pages that used to render under /reference/ and carry noindex are gone.
  • The Workspace Manager's reference left Internals: a package's API is not an implementation note.
  • MentionContext gained symbolHref and moduleHref. api-model cannot know which version a port publishes, so the site injects builders that do, and both prose linkers use them.
  • Each port shell writes its own objects.inv beside its reference; the root build still writes the per-port copies and the combined one, so external intersphinx keeps resolving.
  • Canonicals follow the port rule now: a page is canonical to itself under the default version, and another version of it points there.

What the assembly caught

Each of these was a real defect, found by rebuilding and fixed at its source:

Broken links Cause
1,465,671 withRoot returns the locale root, not the shell's base — Astro's base prefixes routes, not strings a component builds
35,092 a core page linked Workspace and MCP types at its own root, and the sidebar tree still listed them
320 the rehype plugin re-rooted an href its injected builders had already rooted, giving /en/en/…
18 ProseText resolved mentions without those builders; ten prose links and one component still said ../api/
12,600 canonicals the route passed no port to the layout, so every page declared the URL it used to have

check-links also stopped excusing a whole port tree as vendored generator output. Only the native api/ subtree is Sphinx's or docfx's; the reference beside it is ours, and excusing it would have hidden every one of the above.

Verification

  • Full assembly: 15,648 pages, 2,599,081 links, 0 broken.
  • 764 source-level tests, 236 api-model tests, astro check 0 errors, lint clean.
  • check-sidebar-refs, check-api-fidelity, check-xrefs and the negative tests for canonicals, xrefs, type-links and edge extensions all pass.

tony added 12 commits September 13, 2026 15:44
why: /reference/go/ put the Go API outside the Go documentation, above
the version it documents, and mixed three separately versioned packages
-- the core library, the Workspace Manager and the MCP server -- into
one tree. A reference documents one release of one package.
what:
- referenceUrl(port, version, product) and referenceSegment() build
  every reference path, and productApiPath spells the product trees the
  same way: mcp/reference and workspace/reference
- The reference route renders inside each port's shell, where Astro's
  base is already /<port>/<version>/, so its paths carry no port segment
  and the core tree holds core symbols alone
- The Markdown twins and tree.json follow it; the root build keeps the
  cross-port hub at /reference/ and the A-Z symbol index
- referenceHref resolves a cross-port link through the target port's
  default version, which defaultVersionFor reads from the assembly's
  LIBTMUX_DOCS_PORT_DEFAULTS
why: A prose mention resolved through api-model, which cannot know which
version a port publishes or which package a symbol belongs to, so every
cross-reference in prose still named the old path. The intersphinx
inventories named it too.
what:
- MentionContext takes symbolHref and moduleHref; the site passes
  builders that know the target port version and the three trees, and
  tooling that only asks whether a span resolves keeps the default
- Every resolved mention now routes through productApiHref
- A port shell writes its own objects.inv beside its reference; the root
  build keeps the per-port copies and the combined one, so an external
  intersphinx mapping still resolves
- The assembly points its /api/ redirect stubs at the new location
why: The Workspace Manager's declarations answered under
workspace/internals/api and the MCP server's under mcp/api, two
spellings for the same thing and neither matching the core library's.
A reader who has found one reference should be able to guess the others.
what:
- The generated pages and their landing page move together:
  <port>/<version>/workspace/reference/ and .../mcp/reference/
- The workspace reference leaves Internals, because a package's API is
  not an implementation note; Internals keeps its guides, topics and
  examples, and workspaceRedirects lifts those alone
- A page's own port comes from the build rather than from the second
  segment of its path, which no longer carries one
- Checks read the trees through scripts/reference-trees.mjs, which knows
  a port publishes one version prefix or two and that each carries three
  references
- Served checks, dev checks and unit expectations follow the new paths
why: Nine checks and eleven suites located the reference by the path it
had. Left alone they would measure an empty tree and pass, which is the
one failure mode a gate must not have.
what:
- canonicals, xrefs, type-links and api-fidelity read every port's trees
  through referenceDirs, which covers both version prefixes and all
  three products
- sidebar-refs asserts the entry is /<port>/<version>/reference/ and
  still first; its negative fixture drops that link
- The canonicals negative case drops a version segment rather than a
  port segment, which is the collision the new shape can have
- The switcher test asserted that no port link may carry a reference
  path, which is now the shape itself: it asserts each language link
  offers that port's root and nothing deeper
- check-links no longer excuses a whole port tree as vendored output.
  Only the native `api/` subtree is Sphinx's or docfx's; the reference
  beside it is ours
- Served checks, dev checks, fixtures and the publication smoke test
  follow the new paths
why: A signature on a core page can name a Workspace Manager or MCP
type, and the page built that link from its own root -- 35,092 links to
pages that moved. The sidebar tree listed those declarations too, since
the nav sidecar covers the whole model.
what:
- The reference page resolves every symbol through productApiHref, so a
  product type links into its own package reference
- navTree lists what the core reference holds, dropping declarations
  that belong to a product tree
why: Every mention records the URL it links to, and all 775 moved.
what:
- gen-mentions --check passes again
why: Thirty-three prose links and three notes named the old path.
what:
- Each link is rewritten to the tree that holds that symbol: a
  Workspace Manager type to workspace/reference, an MCP one to
  mcp/reference, the rest to the core reference under the port version
- The parity note and the edge-function example follow
why: The plugin added the site root to an href the injected builders had
already rooted, so every module link read /en/en/… -- 320 broken links
from prose. Twenty-four pages also pointed at ./api/, the product page
that became a reference of its own.
what:
- Only the package default, which carries no root, gets one added
- The relative prose links follow the product reference out of Internals
why: ProseText resolved a mention through api-model and rooted the
unversioned default, so a port landing page linked six symbols at the
old path. Ten prose links and one component still named ../api/.
what:
- ProseText injects the same symbolHref and moduleHref the rehype plugin
  does, so both spell a reference URL one way
- The MCP tool page and the last relative links point at the reference
  beside them
why: The route passed no port to the layout, so every page declared
/en/reference/<symbol>/ -- the URL it used to have, which no longer
exists. 12,600 pages pointed there.
what:
- The route passes its port and version, so the canonical is built the
  way every other port page builds one
- check-canonicals asserts the port rule it now follows: a page is
  canonical to itself under the default version, and another version of
  it points there
why: The defaults lookup ran above the argument it reads, so
check-canonicals threw before it checked anything -- and its negative
test is what caught it.
what:
- The lookup moves below the site directory it depends on
why: The preview build publishes one version per port, so a prose link
that spelled /py/stable/reference/ broke there while passing on an
assembly that builds both. CI caught it: one broken link in 1,987,189.
what:
- Every same-port reference link in prose is relative to the page, so it
  resolves under whichever version prefix the build produced
- The type-name ceilings move to the wider basis: the check reads all
  three trees per port now, where before it read only the core tree and
  never saw a product page
why: A product reference is a section of its product now, so the
assembled suite looked for pages under the path it had, and the
structured-data rule named api and tools but not reference.
what:
- The product section list carries reference beside Internals rather
  than inside it, and the workspace breadcrumb reads Reference
- Seo marks any reference page APIReference, the core tree included,
  where before only a product api or tools page qualified
- The exported metadata assertion follows the same spelling
why: Four files still named workspace/internals/api, and the
intersphinx writer looked for an inventory at the root copy alone.
what:
- check-dev, the font audit and the product suite name the product
  reference section
- add-intersphinx prefers the versioned inventory beside its reference,
  falling back to nothing rather than to a path that no longer holds one
why: docs.json named /reference/<port>/ for every port, so sixteen
manifest entries pointed at pages that no longer exist -- the one file
an agent reads first. The native navigation manifest test and the
workspace redirect expectations named the old shape too.
what:
- Each port entry, its Markdown twin and its inventory carry the port's
  default version, the same one the pages are built under
- A product reference is a section of its product, so no legacy twin
  redirects to it and the sitemap lists it like any other page
why: A product reference hangs directly off its product now, so a
symbol page has no Internals level in its breadcrumb, and the search
cases named two pages that no longer exist.
what:
- The breadcrumb assertion pins the product and the page, not the level
  between them that moved
- The search cases name the trees that exist: a product declaration, the
  core reference, its index and the hub
why: The check counted one tree per port and that tree held everything,
duplicates included. It reads the core reference and both product
references now, which is every page a port publishes -- so seven ports
rise, Python falls, and neither movement is a change in resolution.

Python falls because its 627 workspace and MCP declarations left the
core tree for product pages. Both render the same ApiEntry, but a core
reference page wraps it in examples, related entries and module links,
and those carry cross-references the product page has no room for. The
duplicates that used to be counted are gone with them.
what:
- py 13,027 -> 11,925, and the seven that rose take their new counts:
  dotnet 3,975 -> 4,771, swift 3,298 -> 3,739, rs 3,750 -> 4,330
@tony
tony deployed to docs-preview September 13, 2026 22:22 — with GitHub Actions Active
@tony
tony merged commit a25cb52 into main Sep 13, 2026
8 checks passed
@tony
tony deployed to docs-preview-cleanup September 13, 2026 22:41 — with GitHub Actions Active
@tony
tony deleted the api-route-fix branch September 13, 2026 22:48
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.

1 participant