Skip to content

Generate the SDK landing page section lists from sidebars.js - #5179

Draft
DABH wants to merge 5 commits into
mainfrom
docs/prototype-sidebar-link-codegen
Draft

Generate the SDK landing page section lists from sidebars.js#5179
DABH wants to merge 5 commits into
mainfrom
docs/prototype-sidebar-link-codegen

Conversation

@DABH

@DABH DABH commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Draft for #5144. The approach works and is verified end to end; what needs a decision before this merges is listed at the bottom.

The problem, as of this week

#5118 added 19 Serverless Workers pages across 7 SDKs on Aug 20. Landing-page links to that feature afterwards:

SDK pages landing-page links
go 3 1
java, python, typescript, dotnet 3 each 0
ruby, rust 2 each 0

A documented feature was unreachable from six of seven SDK landing pages. That is the issue in one data point, and it happened after the analysis that prompted this PR — not historically.

What this does

Wraps each ## [Section](url) bullet list on the 8 landing pages in markers, and fills it from sidebars.js:

## [Workers](/develop/python/workers)

{/* SIDEBAR-LIST-START section="/develop/python/workers" */}
- [Worker processes](/develop/python/workers/run-worker-process)
- [Interceptors](/develop/python/workers/interceptors)
- [Serverless Workers](/develop/python/workers/serverless-workers)
{/* SIDEBAR-LIST-END */}

yarn sidebar-links regenerates; yarn check:sidebar-links fails CI when a list is stale.

This run adds 11 links, including Serverless Workers to all six SDKs that were missing it:

SDK added
dotnet Serverless Workers, Standalone Operations
java Serverless Workers, Error handling
python Interceptors, Serverless Workers, Error handling
ruby Serverless Workers
rust Serverless Workers
typescript Serverless Workers, Standalone Activities

Insert-only, deliberately

The sidebar decides which pages appear. It does not decide the link text or the order.

I built full generation first and measured it. On the section index pages it would have fixed 5 gaps at the cost of 7 deleted links — including an external learn.temporal.io tutorial living inside a section list — and 37 rewritten labels, several against the sentence-case rule in readme/STYLE.md (Feature guideFeature Guide, Side effectsSide Effects). The sidebar even contradicts itself: one page wanted Asynchronous Activity completion...Completion, another the reverse.

So an entry already listed keeps its text and position, a missing entry is inserted next to its sidebar sibling, and a line the sidebar doesn't know about is preserved.

Full generation Insert-only (this PR)
Gaps fixed 11 11
Links deleted 7+ 0
Labels rewritten 37 0
Diff churn across every page 125 insertions, 0 deletions

Python's Integrations section keeps Langfuse, OpenBox, Parseable, Pydantic AI and Tenuo, none of which are in the sidebar.

Why generated text, not a React component

scripts/mdx-to-md.mjs:100 registers DocCardList as strip-block. A component would blank these links in llms-full.txt and every per-page .md unless someone also wrote and maintained a scripts/component-handlers/ handler — and readme/MARKDOWN_PIPELINE.md:174-193 already flags that component/handler duplication as debt.

This is not hypothetical. GuidesGrid was never wired in, so build/guides.md is 225 bytes with zero guide links today and readme/COMPONENT_REGISTRY.md:48 marks it UNKNOWN.

Markers are MDX expression comments, which transformMdx already strips, so the bullets survive as ordinary Markdown. Verified in the built output:

build/develop/java/index.html   serverless-workers  ✓
build/develop/java.md           serverless-workers  ✓
build/develop/java.md           SIDEBAR-LIST        0  (stripped)

Untouched

Headings that are not sidebar categories — ## Install and get started, ## Temporal <X> technical resources, ## Get connected with the Temporal <X> community — get no markers, so their 58 external links stay hand-written. Nested leaves (serverless-workers/aws-lambda, cloud-run) stay one click deeper, which is the existing convention for a landing page.

Coverage reporting

Markers are opt-in, so a green check with thin coverage would read as "no drift" when it means "no coverage." --check therefore also lists pages still hand-maintaining a list:

Note: 42 page(s) still hand-maintain a link list with no markers, so this check does not cover them:
  docs/develop/go/workflows/index.mdx  [Workflows, 13 links]
  ...

A warning, not a failure: a page with real prose is allowed a hand-written list, and marker-absence is how it says so. That is also why this needs no baseline file, unlike bin/orphan-pages-baseline.json.

Checks

  • yarn build passes; new links verified in both the HTML and the LLM .md
  • yarn test — 9 new tests in bin/generate-sidebar-link-lists.test.js, plus a case asserting every SDK section index page resolves to a sidebar category
  • yarn check:orphans, yarn lint:mermaid, check-redirects-for-moved-pages: clean
  • vale --config .vale-ci.ini on the 8 changed pages: 0 errors, 0 warnings. One pre-existing suggestion on dotnet/index.mdx (Temporal .NET technical resources) is unchanged from main — line number shifted only. It looks like a Vale tokenization artifact on .NET, worth a look alongside Add missing proper nouns to the Temporal.Headings exception list #5140.

Decisions this needs

  1. Is generating these lists the direction you want, versus the issue's other branch (replace them with prose)? I researched both; the evidence against hand-written per-SDK prose is that go/index.mdx has a prose block no other SDK ever got in 4.5 months, a tracked 8-SDK sweep landed "non-retryable by design" on 7 of 8 (ruby missed), and of 117 commits touching an SDK landing page in the last 18 months, 52 (44%) touched exactly one and 3 (2%) touched all eight. Also relevant: onBrokenLinks: 'throw' means every link here is machine-checked, and prose would not be.
  2. Should check:sidebar-links block CI or advise? It is wired as a script only; no workflow added yet.
  3. Roll the 42 section index pages in too? Separate, larger diff — deliberately not bundled.

Review notes (two passes over this PR)

Five defects found and fixed in abb4d4c

Defect Consequence
preserved count was existing.length - sidebarUrls.size went negative (-3) whenever the sidebar had more entries than the page listed
Blank lines inside a marked region were dropped "insert-only" was quietly destructive on a region with deliberate spacing
Label-form section= resolved ambiguous matches silently Quickstart exists under several sections; it picked whichever came first in traversal. Now refuses and points at the URL form
Coverage reporting skipped pages with < 2 links hid 10 pages, four of them workers/index.mdx — the pages most likely to be missing an entry, since a one-link list is thinner than a ten-link one. Coverage reporting went 42 → 52 pages
Not-found error said no category labelled misleading once markers accepted URLs

processFile had no tests

It has five now: unmatched SIDEBAR-LIST-END, a page no sidebar category links to, the guarantee that content outside the markers never changes, no-op on an already-current page, and URL-form resolution where heading text differs from the sidebar label. Suite is 14 tests.

The honest cost: 116 marker lines for 12 link lines

The "125 insertions, 0 deletions" figure above is accurate but flattering. Broken down:

marker lines added : 116
link lines added   :  12

93% of this diff is scaffolding. The case for paying it is that markers are a one-time cost that converts an ongoing manual burden into an enforced one — 11 links today, but the drift rate is what matters, and #5118 shows it recurring. The case against is 116 lines of comment noise in reader-facing source for 12 links.

A marker-free alternative exists and is worth weighing before this merges: infer the managed region from the ## [Section](url) heading itself, since on a landing page every linked-H2 bullet list is a sidebar mirror. That would delete all 116 lines. The trade is that it becomes implicit — a page gets no way to say "this list is deliberately hand-written," which is exactly what marker-absence expresses today, and it's why the 16 content-bearing section index pages can opt out for free. I have not built it; it is a design fork rather than a defect, and it is a reasonable thing to prefer.

Verified across all 8 SDKs, not just one

Earlier I had only checked Java. All eight landing pages: markers absent from the rendered HTML and the LLM .md, with 22-55 bullets surviving in each .md.

DABH added 3 commits August 24, 2026 23:54
Markers are opt-in, so a green check with low coverage reads as no drift
when it means no coverage. Warn (not fail) about section index pages that
still hand-maintain a link list, so the gap is visible and new pages get
noticed.
The eight SDK landing pages restate the sidebar by hand, so adding a page
wires it into sidebars.js and leaves the landing page behind. #5118 is the
current example: it added Serverless Workers pages across seven SDKs, and
six of the seven landing pages never mentioned the feature.

Wraps each `## [Section](url)` list in markers and fills it from the same
sidebar Docusaurus builds the left nav from. This run adds 11 missing
links, including Serverless Workers to all six SDKs that were missing it.

Insert-only on purpose. The sidebar decides which pages appear; existing
lines keep their text and their position, and a line the sidebar doesn't
know about is preserved rather than deleted. Generating labels from the
sidebar would have rewritten 37 of them, several against the sentence-case
rule in readme/STYLE.md, and would have deleted the external links inside
the Integrations sections. The diff here is 125 insertions and no
deletions.

Markers are MDX expression comments, so scripts/mdx-to-md.mjs already
strips them while the bullets survive as ordinary Markdown. That is why
this generates text in place instead of rendering a component: DocCardList
is registered as strip-block, and an unwired GuidesGrid already leaves
build/guides.md at 225 bytes with no links.

Sections whose heading is not a sidebar category — Install and get
started, technical resources, community — carry no markers, so their 58
external links stay hand-written.
Copilot AI balanced review requested due to automatic review settings August 25, 2026 04:58
@vercel

vercel Bot commented Aug 25, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
temporal-documentation Ready Ready Preview Aug 25, 2026 6:19am

Request Review

@github-actions

github-actions Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

- [Converters and encryption](/develop/dotnet/best-practices/data-handling)
{/* SIDEBAR-LIST-END */}

## Temporal .NET technical resources

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 [vale] reported by reviewdog 🐶
[Temporal.Headings] 'Temporal .NET technical resources' should use sentence-style capitalization.

@DABH

DABH commented Aug 25, 2026

Copy link
Copy Markdown
Contributor Author

Correcting a number in the description above.

It originally cited "65% of landing-page commits touch exactly one page while 1.3% touch all eight." I re-measured that myself and it was wrong:

git log --since="2025-02-24" --format="%H" -- 'docs/develop/*/index.mdx' | sort -u
# then per commit, count matches of ^docs/develop/[a-z]+/index\.mdx$

total commits touching an SDK landing page : 117
touched exactly one                        :  52  (44%)
touched eight or more                      :   3  (2%)

So 44%, not 65%, over a larger commit set than the original figure used (117 vs 79 — the two passes used different windows). Description updated to the verified numbers.

The conclusion is unchanged and does not depend on the exact figure: parallel edits across all eight landing pages are rare, single-SDK edits are the norm, and that is the pattern this PR removes the need for. The primary evidence remains #5118 — a feature documented across seven SDKs that six of seven landing pages never mentioned.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Generates SDK landing-page link lists from sidebar data to prevent navigation drift.

Changes:

  • Adds sidebar-list generation and validation commands with tests.
  • Marks generated regions across eight SDK landing pages.
  • Adds 11 missing links, including Serverless Workers links.

Reviewed changes

Copilot reviewed 10 out of 12 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
package.json Adds generation and check commands.
bin/generate-sidebar-link-lists.js Implements sidebar-list reconciliation.
bin/generate-sidebar-link-lists.test.js Tests generation behavior and sidebar resolution.
docs/develop/go/index.mdx Marks generated SDK section lists.
docs/develop/java/index.mdx Marks lists and adds missing links.
docs/develop/dotnet/index.mdx Marks lists and adds missing links.
docs/develop/php/index.mdx Marks generated SDK section lists.
docs/develop/python/index.mdx Marks lists and adds missing links.
docs/develop/python/workers/index.mdx Marks the list and adds Serverless Workers.
docs/develop/ruby/index.mdx Marks lists and adds Serverless Workers.
docs/develop/rust/index.mdx Marks lists and adds Serverless Workers.
docs/develop/typescript/index.mdx Marks lists and adds missing links.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

{/* SIDEBAR-LIST-START */}
- [Worker processes](/develop/python/workers/run-worker-process)
- [Interceptors](/develop/python/workers/interceptors)
- [Serverless Workers](/develop/python/workers/serverless-workers)
- The preserved count used existing.length - sidebarUrls.size, which went
  negative whenever the sidebar had more entries than the page listed.
- Blank lines inside a marked region were dropped, so "insert-only" was
  quietly destructive on a region with deliberate spacing.
- A label-form section= silently resolved to whichever same-named category
  came first in traversal order. "Quickstart" appears under several
  sections, so this now refuses and points at the URL form.
- Coverage reporting skipped pages with fewer than two links, which hid 10
  pages including four workers/index.mdx — the pages most likely to be
  missing an entry, since a one-link list is thinner than a ten-link one.
- The not-found error said "no category labelled" even when the marker had
  supplied a URL.

Also adds five processFile tests. That function had none, so marker
parsing, the unmatched-END and unresolvable-page errors, and the promise
that content outside the markers never changes were all unverified.
This PR marked docs/develop/python/workers/index.mdx but left the same gap
on the other five, so it claimed section index pages were out of scope
while including one of them. All six now carry the link, which makes the
scope coherent: the Serverless Workers gap is closed at both the landing
page and the section index level. The other 46 section index pages remain
the separate sweep.
@DABH

DABH commented Aug 25, 2026

Copy link
Copy Markdown
Contributor Author

Responses to the two review comments.

@copilot on python/workers/index.mdx — fixed in 57f11f4

Good catch, and it was a real incoherence rather than just a missing file. This PR said section index pages were out of scope, then included python/workers/index.mdx anyway — it happened to be the page I prototyped the markers on. So five pages had the identical gap while one did not, for no defensible reason.

All six now carry it: dotnet, java, python, ruby, rust, typescript. 15 insertions, 0 deletions, idempotent on re-run, and verified in the built output — the link appears in both the rendered HTML and the per-page .md for the LLM corpus on all five newly-marked pages.

The scope is now coherent as "close the Serverless Workers gap at both the landing-page and section-index level." The remaining 46 section index pages stay the separate sweep noted in the description, since those need the list-depth question answered first.

reviewdog/vale on dotnet/index.mdx:102 — false positive in the rule, not a heading defect

It is pre-existing: docs/develop/dotnet/index.mdx:86 on main carries the identical message. It only surfaced here because this PR's insertions shifted it into filter_mode: diff_context range.

The mechanism, probed directly:

Heading Flagged
Temporal Go technical resources no
Temporal .NET technical resources yes
Temporal NET technical resources yes

Vale tokenizes away the leading dot, so NET reads as a stray capitalised word and the .NET entry already in Temporal.Headings's exception list never matches. The heading is already sentence case; nothing about it is wrong.

Rewording the heading would paper over a rule bug that will keep firing on every .NET page. The root fix is adding NET to the exception list, which is #5140's subject, so I am doing it there rather than here.

DABH added a commit that referenced this pull request Aug 25, 2026
Vale tokenizes the leading dot away, so the ".NET" entry never matches the
word it actually sees. Every sentence-case heading naming the SDK was
therefore flagged: "Temporal .NET technical resources" is reported while
the identical Go heading is not.

Clears 8 false positives (367 -> 359) without weakening the rule --
"Temporal .NET Technical Resources" is still flagged, as is any other real
title-case heading.

Surfaced by reviewdog on #5179, where the pre-existing hit on
docs/develop/dotnet/index.mdx moved into diff context.
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.

2 participants