Skip to content

ci: validate internal documentation anchors - #784

Merged
brendanjryan merged 2 commits into
mainfrom
brendanjryan/ci-anchor-links
Jul 31, 2026
Merged

ci: validate internal documentation anchors#784
brendanjryan merged 2 commits into
mainfrom
brendanjryan/ci-anchor-links

Conversation

@brendanjryan

@brendanjryan brendanjryan commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Motivation

Internal documentation links can target headings that no longer exist because Vocs validates the target page but not its fragment.

Supersedes #743 while preserving the original contributor's link fixes and commit attribution.

Summary

  • preserve the broken-anchor corrections from docs: fix broken anchor links #743 and repair one additional TIP-20 fragment
  • add MDX-aware internal anchor validation for Markdown links and static JSX href/to attributes
  • run anchor validation in the required Verify workflow

Key design considerations

  • expand imported MDX snippets where their components render so snippet headings are valid targets
  • generate heading fragments with GitHub slugger semantics, including duplicate headings
  • leave missing-page validation to Vocs and keep this check deterministic and offline

Several internal links pointed to anchors that do not exist on their
target pages, so they landed at the top of the page instead of the
intended section. Most were headings that had since been renamed —
for example "Handling ETH Balance Checks" became "Handling ETH (Native
Token) Balance Checks", and "Rebalance liquidity" became "Rebalance
pools".

Point each link at the anchor that actually exists. Link text and
surrounding prose are unchanged.
@vercel

vercel Bot commented Jul 31, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
tempo-docs Ready Ready Preview Jul 31, 2026 4:00pm

Request Review

@brendanjryan
brendanjryan force-pushed the brendanjryan/ci-anchor-links branch from 15d2d36 to 4959c9e Compare July 31, 2026 15:56
@brendanjryan
brendanjryan marked this pull request as ready for review July 31, 2026 16:01
@brendanjryan
brendanjryan merged commit a90dce3 into main Jul 31, 2026
12 checks passed
@brendanjryan
brendanjryan deleted the brendanjryan/ci-anchor-links branch July 31, 2026 16:04

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4959c9eaec

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

const slugger = new GithubSlugger()
const anchors = new Set<string>()
for (const event of eventsByRoute.get(page.route) ?? []) {
if (event.type === 'heading') anchors.add(headingAnchor(event.text, slugger))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Parse explicit heading anchors before slugging

When a page follows the prescribed rename pattern ## Tempo API pagination modes {#modes}, nodeText includes {#modes} in the heading text and the checker records a generated slug such as tempo-api-pagination-modes-modes instead of the explicit modes ID. A preserved link to #modes then fails the required Verify workflow even though the heading deliberately retains that deep link; extract explicit heading IDs rather than passing the suffix to GithubSlugger.

AGENTS.md reference: AGENTS.md:L66-L70

Useful? React with 👍 / 👎.

}

function targetFor(href: string, page: Page): { route: string; fragment: string } | undefined {
if (/^(?:https?:|mailto:|tel:)/.test(href)) return

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Validate canonical absolute documentation links

For canonical links such as https://tempo.xyz/developers/docs/...#fragment, this early return classifies the URL as external before the existing /developers/docs normalization can run. The repository already has these internal fragment links in src/pages/docs/protocol/tips/tip-1020.mdx and src/pages/docs/protocol/tip20/spec.mdx, so a target heading can be renamed and leave them stale without this required check reporting anything; recognize the canonical docs origin and normalize its mount before filtering external URLs.

AGENTS.md reference: AGENTS.md:L7-L10

Useful? React with 👍 / 👎.

import path from 'node:path'
import { checkInternalAnchors } from '../src/lib/internal-anchor-links.ts'

const result = checkInternalAnchors(path.resolve('src/pages'))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Include redirect fragments in anchor validation

Because the command passes only src/pages to the checker, fragment-bearing redirect destinations in src/lib/docs-routing.ts and vocs.config.ts never become link events. Existing mappings such as docs-routing.ts:30 and vocs.config.ts:1281 can therefore point at a renamed heading indefinitely while this required check passes, silently breaking the legacy deep links those redirects preserve; include redirect destinations in the anchor candidates and validate their fragments against the canonical pages.

AGENTS.md reference: AGENTS.md:L9-L10

Useful? React with 👍 / 👎.

if (!fs.existsSync(importedFile)) {
throw new Error(`${file}:${line} imports missing Markdown file ${importedFile}`)
}
events.push(...sourceEvents(importedFile, [...stack, file]))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reset slug generation at imported MDX boundaries

Imported .mdx files compile as separate modules, so Vocs' heading slugger resets for each file, but flattening imported events here makes the checker slug every heading in one host-page sequence. If a host page and its imported snippet both contain ## Setup, the rendered DOM has two id="setup" headings, while this checker invents setup-1 for the snippet and incorrectly accepts a link to that nonexistent fragment; preserve a separate slugger scope for each compiled MDX file when expanding shared snippets.

AGENTS.md reference: AGENTS.md:L20-L22

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants