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:
- existing documents continue to work unchanged
- documents with duplicate headings receive persisted heading identities on the next
generate
- 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
Problem
When multiple headings have the same visible title,
mdtoccurrently 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:
In those cases, a previously published external link may start pointing to a different duplicate heading after a later
generateorregenrun.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:
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:
or another compact, repository-internal marker format.
Generation rules:
installation-7f3aStrip behavior
This needs an explicit contract:
stripshould probably keep heading identity metadata soregencan reconstruct stable duplicate anchorsstrip --rawshould remove all heading identity metadata together with other managed artifactsWithout this distinction,
strip -> regenwould lose the stability benefit.Migration strategy
A compatible migration path could be:
generateOpen questions
generateandregen, or also acrossstrip?Suggested acceptance criteria
regenpreserves stable duplicate anchorsstripandstrip --rawhave clearly documented, tested semantics regarding heading identity metadata