Skip to content

Flattened PDF/DOCX build collapses duplicate section anchors, silently misrouting intra-doc links #106

Description

@asluk

Summary

When doc_build flattens multiple source Markdown files into a single document, two headings that share a title (in different source files) collapse to the same slug anchor. An intra-document link ([...](#some-heading)) then resolves to the first occurrence in flatten order instead of the intended section. The build succeeds with no warning.

Minimal self-contained repro (no external repo needed):

a.md

# Doc A
## Shared Heading
A's section.

b.md

# Doc B
See [Shared Heading](#shared-heading).
## Shared Heading
B's section.

Build a.md + b.md through the flatten pipeline in that order. In the output, the link in Doc B — which targets its own "Shared Heading" — instead jumps to Doc A's section, because both slug to #shared-heading and the first occurrence wins. Swap the order and the bug moves with it, confirming it's flatten-order-dependent, not content-dependent.

How we found it

Reported on aousd/geometry-wg (PR #48, by @miguelh-nvidia): two different schema files there each define a ## Velocity-Based Interpolation section, and one file's self-referential links land in the other schema in the built PDF. Caught by manual PDF inspection, not CI. Linked here for provenance — the minimal repro above reproduces it standalone, so no access to that repo is needed to investigate. The pattern generalizes: parallel schema docs intentionally reuse titles like Overview, Validation, Example, Extent, Properties of …, so any bare same-slug cross-reference among them is silently misrouted, and duplicates without a cross-reference are latent.

Why the build gate didn't catch it

The gate confirms the artifact builds; it does not validate anchor uniqueness or link resolution, so a broken cross-reference passes CI.

Proposed fix (either or both)

  1. Namespace anchors per source file during flatten — prefix generated anchors with the source-file stem (e.g. a__shared-heading) and rewrite each file's intra-file links to match, so identical titles in different files stay distinct. Fixes all current and future collisions without changing source prose.
  2. Add an anchor/link check to the build gate — fail (or warn) on a duplicate anchor that is targeted by a link, and on any link whose target anchor is missing/ambiguous. Catches regressions at PR time.

Option 1 is the durable fix; option 2 prevents recurrence.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions