Skip to content

Stabilize duplicate-heading anchors with persisted heading identities #8

@rokath

Description

@rokath

Problem

When multiple headings have the same visible title, mdtoc currently has to disambiguate generated anchors by position or occurrence order.

That makes duplicate-heading anchors only position-stable, not edit-stable.

Examples that can break external links:

  • two identical headings are reordered
  • a new identical heading is inserted before an existing one
  • one identical heading is removed, changing later duplicate numbering

In those cases, a previously published external link may start pointing to a different duplicate heading after a later generate or regen run.

Why this matters

For unique headings, title-derived slug anchors are stable and predictable.
For duplicate headings, however, order-based suffixes like -1, -2, ... are not stable under common editing operations.

This is especially relevant for:

  • documentation linked from other repositories
  • long-lived bookmarks
  • references from issue trackers, changelogs, blog posts, or release notes

Non-goal

This issue is not about introducing random suffixes.

Random or pseudo-random anchor suffixes would weaken the current deterministic and idempotent behavior unless they are persisted. Once persistence is required, the real problem is no longer randomness, but stable heading identity.

Proposed direction

Introduce persistent per-heading identities for duplicate-heading stability.

Core idea

Each managed heading may carry a hidden, persisted identity that survives later regeneration.
That identity becomes the basis for duplicate disambiguation.

This avoids order-dependent duplicate suffixes as the primary identity mechanism.

Important design requirement

Persisting duplicate information only inside the managed container is not sufficient.

If two identical headings are reordered, a container-level list cannot reliably tell which heading instance is which.
For true stability under reordering, the identity has to live close to the heading itself.

Implementation sketch

Use heading-local hidden metadata, for example:

<!-- mdtoc anchor: 7f3a -->
## Installation

or another compact, repository-internal marker format.

Generation rules:

  • unique headings may still use plain title-derived slug anchors as today
  • duplicate headings receive a persisted identity
  • rendered anchors become based on title slug plus persisted ID, for example installation-7f3a
  • if a heading already has a valid persisted identity, reuse it
  • if a duplicate heading has no persisted identity yet, create one once and persist it

Strip behavior

This needs an explicit contract:

  • strip should probably keep heading identity metadata so regen can reconstruct stable duplicate anchors
  • strip --raw should remove all heading identity metadata together with other managed artifacts

Without this distinction, strip -> regen would lose the stability benefit.

Migration strategy

A compatible migration path could be:

  1. existing documents continue to work unchanged
  2. documents with duplicate headings receive persisted heading identities on the next generate
  3. unique headings remain unchanged unless the final design intentionally standardizes all headings on persisted IDs

Open questions

  • Should persisted heading IDs be added only for duplicates, or for all managed headings?
  • What exact hidden marker syntax is least intrusive and easiest to parse safely?
  • Should IDs be short hex strings, base32 fragments, or another compact deterministic-once-persisted format?
  • Should duplicate heading stability be guaranteed only across generate and regen, or also across strip?

Suggested acceptance criteria

  • Reordering two identical headings does not change their persisted disambiguating IDs
  • Inserting a new identical heading before existing ones does not retarget previously generated duplicate anchors
  • regen preserves stable duplicate anchors
  • strip and strip --raw have clearly documented, tested semantics regarding heading identity metadata
  • the implementation remains deterministic and idempotent

Metadata

Metadata

Assignees

No one assigned

    Labels

    documentationImprovements or additions to documentationwontfixThis will not be worked on

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions