Skip to content

Republish the course to fix sidebar links going to /docs instead of /learn - #29

Closed
RafaelJaime wants to merge 2 commits into
huggingface:mainfrom
RafaelJaime:fix/pin-doc-builder-learn-sidebar-links
Closed

RafaelJaime wants to merge 2 commits into
huggingface:mainfrom
RafaelJaime:fix/pin-doc-builder-learn-sidebar-links

Conversation

@RafaelJaime

@RafaelJaime RafaelJaime commented Sep 30, 2026 •

Copy link
Copy Markdown

Problem

Every link in the sidebar of https://huggingface.co/learn/context-course navigates to the /docs/... URL space instead of /learn/...:

Expected https://huggingface.co/learn/context-course/unit1/skill-format
Actual https://huggingface.co/docs/context-course/main/en/unit1/skill-format

The same happens for the prev/next footer links. Pressing the browser Back button lands on the correct /learn/... URL, which is a useful clue about where the rewrite happens.

Root cause

The server-rendered HTML is correct — the anchors really do point at /learn/...:

$ curl -s https://huggingface.co/learn/context-course/unit1/skill-format \
    | grep -o 'href="/learn/context-course/unit1/[^"]*"' | sort -u
href="/learn/context-course/unit1/building-skills"
href="/learn/context-course/unit1/introduction"
href="/learn/context-course/unit1/quiz1"
...

The rewrite is done client-side by the doc-builder SvelteKit app bundled into the published build. This chunk is live for context-course right now:

// https://huggingface.co/docs/context-course/main/en/_app/immutable/chunks/4N2m5NWB.js
function d(t){if(/^\/(docs|learn)/.test(t)){
  const s=t.slice(1).split("/"), e=s.shift(), n=s.shift(),
    o=(e==="learn"?/^(?:pr_\d+)$/:/^(?:(master|main)|v[\d.]+(rc\d+)?|pr_\d+)$/).test(s[0])?s.shift():"main",
    r=/^[a-z]{2}(-[A-Za-z]{2})?$/.test(s[0])?s.shift():"en", i=s.join("/");
  if(n==="context-course"&&o==="main"&&r==="en")
    return `/docs/context-course/main/en/${i}`   // <-- here
}}

That is the pre-fix getHfDocFullPath() from kit/src/lib/hfDocPaths.js. Its caller in kit/src/routes/+layout.svelte momentarily rewrites the clicked anchor's pathname in the DOM so SvelteKit's own click handler resolves it as an internal route:

const shorthandPathname = anchor.pathname;
const fullPath = getHfDocFullPath(shorthandPathname);
if (fullPath && fullPath !== shorthandPathname) {
    pendingShorthand = { href: anchor.href, fullPath };
    anchor.pathname = fullPath;          // /learn/... -> /docs/.../main/en/...
    setTimeout(() => { anchor.pathname = shorthandPathname; });
}

For a course page the Hub's own link handler then reads the already-rewritten /docs/... path and requests /api/docs/context-course/main/en/..., which does not exist (courses are served from /api/learn/...), so it falls back to a full navigation to the /docs URL. Back/forward works because it deliberately falls back to a full page load of the shorthand URL, which the server resolves correctly — the caveat is documented in the same component.

The client-side rewrite was introduced by huggingface/doc-builder#792 (Svelte 5 / SvelteKit 2 kit upgrade) and already fixed upstream by huggingface/doc-builder#828 (be7b9d0, 2026-09-24), which stops expanding /learn links:

-	if (/^\/(docs|learn)/.test(pathname)) {
+	if (/^\/docs\//.test(pathname)) {

The published context-course build predates that fix and still ships the broken helper, so the bug is visible today.

Why this needs a change here and not upstream

doc-builder is already fixed — there is nothing left to patch there. The reason the course is still broken is that a published course is a frozen build artifact: the kit JS is compiled into it, so an upstream fix only reaches readers when the course itself is rebuilt.

date event
2026-07-03 doc-builder#792 lands the Svelte 5 kit, introducing the /learn → /docs rewrite
2026-09-18 last Build documentation run for this repo (0b4572f), resolving @main to the regressed kit — course breaks
2026-09-24 doc-builder#828 (be7b9d0) fixes it upstream
2026-09-30 no rebuild since, so the published course still ships the broken helper

Why this repo is affected and the other courses are not

context-course is the only HF course still calling the reusable doc-builder workflows with @main:

repo pinned doc-builder ref
mcp-course 90b4ee2c… # main
agents-course bcff59fc… # main
smol-course 90b4ee2c… # main
context-course @main (floating)

Those pins predate #792, so those courses never received the regressed kit. context-course floats on main, so it picked the regression up on its last build and will keep absorbing whatever lands upstream.

Fix

The actual repair is a rebuild, not a diff. Since this repo calls the reusable workflows with @main, a build started today already resolves to a fixed doc-builder. The problem is that Build documentation only triggers on push to main, so there is no way to republish from the Actions tab — a commit has to land. This PR therefore does three things, in order of importance.

1. Republish the course

Merging lands a commit on main, which triggers Build documentation, which republishes with the fixed kit. That alone makes the sidebar links resolve to /learn/context-course/... again.

2. Make a rebuild possible without a commit (workflow_dispatch)

The fix for this bug lived upstream and still does; the course was broken purely because it had not been rebuilt since. With only a push trigger, "the upstream fix is in, we just need to republish" requires a dummy commit. Adding workflow_dispatch: turns it into a button, as huggingface/transformers already does. This is the change that makes the next occurrence a one-click fix.

3. Pin the reusable workflows

Pin all three workflows to doc-builder 61704e23fa97c0502ce5355c489c6bec4194bff1 (current main, which contains be7b9d0), matching the convention the sibling course repos adopted in their "🔒 pin ... actions to commit SHAs" commits.

This does two things:

  1. Fixes the bug. Merging pushes to main, which triggers Build documentation, which republishes the course with the fixed kit — so the sidebar links start resolving to /learn/context-course/... again.
  2. Prevents the recurrence. A floating @main is exactly how the regression arrived; it is also why GitHub's hardening guide recommends pinning reusable workflows to a full commit SHA, and doc-builder's own build_main_documentation.yml relies on the caller's pin to decide which doc-builder code runs with the build secrets.

This repo already decided to pin, it just never finished the job. 0b4572f ("chore: track GitHub Actions versions with Dependabot", 2026-09-18) added .github/dependabot.yml, which opens with:

Actions are pinned to commit SHAs across the organisation; a pinned SHA is only safe while something raises it, and that something is Dependabot.

Dependabot is configured for github-actions weekly with a 7-day cooldown and grouped PRs — but the three doc-builder uses: refs were left on @main, which Dependabot cannot raise because there is no version to raise. This PR supplies the SHAs that config was written to maintain, so the pins stay current automatically and the usual objection to pinning ("it freezes you out of upstream fixes") does not apply here.

The trailing # main comment follows the existing convention in the other course repos so Dependabot / a future bumper can still tell which branch the SHA came from.

Alternatives considered

Fix it upstream instead. Not available — huggingface/doc-builder#828 already did, six days after this course's last build. Nothing reaches readers until the course republishes.

Just trigger a rebuild, no diff at all. Correct, and it is the part of this PR that actually repairs the site — re-running the 2026-09-18 run would re-resolve @main to a fixed doc-builder and the symptom would go away. Two reasons it is not the whole answer: there is no workflow_dispatch, so a maintainer's only options today are "Re-run jobs" on a six-month-old run or a dummy commit; and it leaves the repo floating on @main, which is exactly how the regression arrived on 2026-09-18. If you would rather keep this minimal, drop commit 2 (the pin) and keep commit 1 — the rebuild and the manual trigger are the parts that matter, and the pin can land separately as the Dependabot follow-up it really is.

Verification

  • Live page HTML confirmed to contain correct /learn/... anchors (so this is not a _toctree.yml issue).
  • Live JS chunk confirmed to contain the pre-#828 getHfDocFullPath (quoted above).
  • git merge-base --is-ancestor be7b9d0 61704e2 confirms the pinned SHA contains the upstream fix; kit/src/lib/hfDocPaths.js at that SHA tests /^\/docs\// only.
  • Build PR Documentation on this PR exercises the two PR workflows with the new pin.

🤖 Generated with Claude Code

RafaelJaime and others added 2 commits September 30, 2026 10:15
Every sidebar (and prev/next) click on huggingface.co/learn/context-course
navigates to https://huggingface.co/docs/context-course/main/en/<page>
instead of https://huggingface.co/learn/context-course/<page>.

The server-rendered HTML is correct — the anchors do point at /learn/... .
The rewrite happens client-side, in the doc-builder SvelteKit app that is
bundled into the published build:

  /* _app/immutable/chunks/4N2m5NWB.js, currently live for context-course */
  function d(t){if(/^\/(docs|learn)/.test(t)){ ... return `/docs/context-course/main/en/${i}` }}

That is the pre-fix `getHfDocFullPath()` from doc-builder's kit. Its caller
in kit/src/routes/+layout.svelte momentarily rewrites the clicked anchor's
pathname in the DOM so SvelteKit treats it as an internal route. For courses
this turns /learn/context-course/unit1/skill-format into
/docs/context-course/main/en/unit1/skill-format before the Hub's own link
handler reads it, so the Hub requests /api/docs/... (404 — courses are served
from /api/learn/) and falls back to a full navigation to the /docs URL.
Going back works because back/forward deliberately falls back to a full page
load of the shorthand URL, which the server resolves correctly.

Upstream already fixed this in huggingface/doc-builder#828 (be7b9d0,
2026-09-24), which stops expanding /learn links. The live context-course
build predates it and still ships the broken helper.

This repo is the only HF course still calling the reusable workflows with
@main, which is how it picked up the regressed kit from doc-builder#792 in
the first place. Pin all three workflows to doc-builder 61704e2 (current
main, contains the fix), matching what mcp-course, agents-course and
smol-course already do. Merging this pushes to main and therefore triggers
a rebuild with the fixed kit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`Build documentation` only triggers on push to main, so the course can only
be republished by landing a commit. That matters here because the fix for
the /learn sidebar links lives in doc-builder, not in this repo: the kit JS
is compiled into the published build, so an upstream fix reaches readers
only when the course is rebuilt.

Add workflow_dispatch so a maintainer can republish from the Actions tab,
as huggingface/transformers already does.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@RafaelJaime RafaelJaime changed the title Fix sidebar links navigating to /docs/context-course/main/en/... instead of /learn/context-course/... Republish the course to fix sidebar links going to /docs instead of /learn Sep 30, 2026
@RafaelJaime

RafaelJaime commented Sep 30, 2026 •

Copy link
Copy Markdown
Author

Closing in favour of #30.

On reflection this repo does not need a diff. The /learn → /docs rewrite was a doc-builder kit regression that huggingface/doc-builder#828 already fixed on 2026-09-24, and build_documentation.yml calls the reusable workflow with @main, so a build started today already resolves to the fixed kit. The published course is simply stale — its last build was 2026-09-18, six days before the fix.

Re-running Build documentation from the Actions tab repairs the site with no change to this repo. #30 has the full root-cause analysis, the live JS chunk showing the pre-fix helper, and the timeline.

The workflow_dispatch trigger and the SHA pins that were in this PR are noted there as optional follow-ups rather than part of the fix — happy to send either as its own PR if you want them.

(edited: the original comment lost two inline code spans to shell expansion)

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