From 2d3fbcd2d21a0a3fa8b4eb7aee1a00581d1f7282 Mon Sep 17 00:00:00 2001 From: Rudy Celekli <47457359+rudycelekli@users.noreply.github.com> Date: Sat, 3 Oct 2026 10:58:25 -0400 Subject: [PATCH] Keep reference heading anchors unique Signed-off-by: Rudy Celekli <47457359+rudycelekli@users.noreply.github.com> --- scripts/validate-reference-links-test.js | 18 ++++++++++++++++++ scripts/validate-reference-links.js | 6 ++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/scripts/validate-reference-links-test.js b/scripts/validate-reference-links-test.js index 7971609a..891e472e 100644 --- a/scripts/validate-reference-links-test.js +++ b/scripts/validate-reference-links-test.js @@ -493,3 +493,21 @@ test('a missing file is reported once, not again for its anchor', () => { assert.match(result.stdout, /L1: references\/renamed\.md — resolves to skills\/hardening\/references\/renamed\.md, which does not exist/); assert.match(result.stdout, /1 skills checked — 1 error\(s\) — FAILED/); }); + + +test('heading slugs remain unique when a literal suffix collides with an allocated slug', () => { + const root = makeSandbox(); + hardeningSkill(root, 'references/patterns.md#example-1-1'); + // A hyphen in the literal heading makes its base slug collide with Example's suffix. + writeFile(root, 'skills/hardening/references/patterns.md', '## Example\n\n## Example\n\n## Example-1\n'); + const result = run(root); + assert.equal(result.status, 0, result.stdout + result.stderr); +}); + +test('a duplicate skips a slug occupied by an earlier literal heading', () => { + const root = makeSandbox(); + hardeningSkill(root, 'references/patterns.md#example-2'); + writeFile(root, 'skills/hardening/references/patterns.md', '## Example\n\n## Example-1\n\n## Example\n'); + const result = run(root); + assert.equal(result.status, 0, result.stdout + result.stderr); +}); diff --git a/scripts/validate-reference-links.js b/scripts/validate-reference-links.js index b9d3b245..b659bcdc 100644 --- a/scripts/validate-reference-links.js +++ b/scripts/validate-reference-links.js @@ -77,8 +77,10 @@ function headingAnchors(file) { const match = line.match(/^ {0,3}#{1,6}[ \t]+(.*?)(?:[ \t]+#+)?[ \t]*$/); if (!match) continue; const slug = slugify(match[1]); - const count = seen.get(slug) || 0; - anchors.add(count === 0 ? slug : `${slug}-${count}`); + let count = seen.get(slug) || 0; + let anchor = count === 0 ? slug : `${slug}-${count}`; + while (anchors.has(anchor)) anchor = `${slug}-${++count}`; + anchors.add(anchor); seen.set(slug, count + 1); } return anchors;