Skip to content

docs: fix dead relative links in notebook pages - #274

Merged
dimitri-yatsenko merged 3 commits into
mainfrom
docs/fix-notebook-relative-links
Sep 10, 2026
Merged

dimitri-yatsenko merged 3 commits into
mainfrom
docs/fix-notebook-relative-links

Conversation

@dimitri-yatsenko

@dimitri-yatsenko dimitri-yatsenko commented Sep 10, 2026 •

Copy link
Copy Markdown
Member

Reported: every link on https://docs.datajoint.com/how-to/read-diagrams/ is dead.

Root cause

mkdocs-jupyter does not rewrite relative links inside notebook markdown cells the way MkDocs rewrites them in .md files. Links written in source-file form are emitted verbatim into the HTML.

From the page URL /how-to/read-diagrams/, ../explanation/entity-integrity.md#schema-dimensions resolves to /how-to/explanation/entity-integrity.md — wrong depth and a source-file extension, so 404.

Notebooks have to use built-site URLs instead: directory-style paths, no .md/.ipynb extension, depth counted from the page URL (one extra level, since each page becomes its own directory).

Why the existing check missed it: the lychee job resolves links against the source tree, where ../explanation/entity-integrity.md genuinely exists — so it passes. Only the built output shows what a reader's browser actually requests.

The reported page

src/how-to/read-diagrams.ipynb — all 5 links:

was now
../explanation/entity-integrity.md#schema-dimensions ../../explanation/entity-integrity/#schema-dimensions
../explanation/entity-integrity.md#dimensions-and-attribute-lineage ../../explanation/entity-integrity/#dimensions-and-attribute-lineage
../reference/specs/diagram.md ../../reference/specs/diagram/
../reference/specs/semantic-matching.md ../../reference/specs/semantic-matching/
../tutorials/basics/02-schema-design.ipynb ../../tutorials/basics/02-schema-design/

Guard against recurrence

scripts/check_links.py checks the built site: every <a href> must resolve to a real file in the build, and any fragment must match an id on the target page. Wired into the Link Check workflow as a second job that builds with --strict and runs it. The source-level lychee job stays — it catches cheaper source rot.

What the guard then found

Running it surfaced links my first grep pass missed, since same-directory links have no ../ prefix to search for:

  • how-to/master-part.ipynb — 5 links (define-tables.md, delete-data.md ×2, insert-data.md, model-relationships.ipynb)
  • how-to/model-relationships.ipynb — 5 links (define-tables.md, design-primary-keys.md, delete-data.md, read-diagrams.ipynb, master-part.ipynb)
  • tutorials/advanced/instances.ipynb — 3 links, source-file form
  • tutorials/advanced/sql-comparison.ipynb — correct form, wrong depth
  • tutorials/basics/01-first-pipeline.ipynb — ../advanced/instances/, wrong depth

It also surfaced pre-existing anchor rot in .md pages, which had to be cleared for the gate to pass:

  • trailing slash inside the fragment — #object-augmented-schemas/, #string-quoting/ — in object-storage-overview, reference/specs/index, migrate-to-v20
  • npy-codec pointed at type-system.md#object--schema-addressed-storage; the id is now #object-objectstore-schema-addressed-storage
  • object-storage-overview pointed at four anchors that don't exist on their target pages

Please sanity-check these four retargets — the old anchors were gone, so I picked the closest existing section rather than inventing headings:

link retargeted to
use-object-storage.md#filepath-references use-object-storage.md#lazy-loading-with-objectref
use-object-storage.md#streaming-access use-object-storage.md#lazy-loading-with-objectref
use-object-storage.md#migration-patterns choose-storage-type.md#migration-between-storage-types
choose-storage-type.md#deduplication choose-storage-type.md#data-not-deduplicated

Verification

Built locally with --strict: zero broken internal links across 142 pages, and MkDocs' own contains a link INFO warnings are gone too.

mkdocs-jupyter does not rewrite relative links inside notebook markdown
cells the way MkDocs rewrites them in .md files, so links written in
source-file form were emitted verbatim into the HTML and 404'd. From
/how-to/read-diagrams/, for example, ../explanation/entity-integrity.md
resolved to /how-to/explanation/entity-integrity.md.

Notebooks must use built-site URLs instead: directory-style paths, no
.md/.ipynb extension, with depth counted from the page URL (one extra
level, since each page becomes its own directory). Most notebooks in the
repo already follow this; these five did not.

- how-to/read-diagrams.ipynb: all 5 links
- how-to/master-part.ipynb: master-part spec
- how-to/model-relationships.ipynb: entity-integrity anchor
- tutorials/advanced/instances.ipynb: 3 links
- tutorials/advanced/sql-comparison.ipynb: right form, wrong depth
@dimitri-yatsenko dimitri-yatsenko added the documentation Improvements or additions to documentation label Sep 10, 2026
The existing lychee check resolves links against the source tree, so it
cannot see this class of bug: a link written as
../explanation/entity-integrity.md is a valid source path and passes,
but mkdocs-jupyter ships it verbatim to the browser, where it 404s.

Add scripts/check_links.py, which checks the built output instead --
every <a href> must resolve to a real file in the build, and any
fragment must match an id on the target page. Wire it into the Link
Check workflow as a second job that builds with --strict and runs it.

Running it surfaced links the first pass missed, since same-directory
links have no ../ prefix to grep for:

- how-to/master-part.ipynb, how-to/model-relationships.ipynb: 9 links
- tutorials/basics/01-first-pipeline.ipynb: instances/, wrong depth

It also surfaced pre-existing anchor rot in .md pages, which had to be
cleared for the gate to pass:

- trailing slash inside the fragment (#object-augmented-schemas/,
  #string-quoting/) in object-storage-overview, specs/index,
  migrate-to-v20
- npy-codec pointed at a type-system id that has since been renamed
- object-storage-overview pointed at four anchors that do not exist on
  the target pages; retargeted to the closest existing sections

Build is now clean under --strict with zero broken internal links
across 142 pages.
setup-python's default cache glob only matches requirements.txt or
pyproject.toml, so the built-site job failed at setup before it could
install anything.
@dimitri-yatsenko
dimitri-yatsenko merged commit f54e746 into main Sep 10, 2026
4 checks passed
@dimitri-yatsenko
dimitri-yatsenko deleted the docs/fix-notebook-relative-links branch September 10, 2026 18:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants