fix: preserve fragments in root spec links - #759
Merged
Conversation
Root-site rewriting left fragments attached to index.md paths, causing links to serve raw Markdown instead of rendered specification pages. Normalize the path before restoring its fragment, and reject internal raw Markdown targets even when version paths are otherwise ignored.
jingyli
approved these changes
Aug 21, 2026
amithanda
approved these changes
Aug 22, 2026
17 tasks
Ectsang
added a commit
to Ectsang/ucp
that referenced
this pull request
Aug 26, 2026
check_links.py decides whether a link is external by matching its scheme
against ("http", "https") plus a four-entry skip list. Anything else falls
through and its path is resolved against the local build, so an off-site
link is reported as a broken internal one:
//example.com/x -> "Not Found" (protocol-relative)
ftp://example.com/x -> "Not Found" (scheme we do not resolve)
vscode:extension/x -> "Not Found" (scheme carrying no host)
Classify by what the URL carries instead: a host, or a scheme we do not
resolve, means off-site. A relative path containing a colon still parses
with no scheme and no host, so it keeps being resolved.
Two adjacent gaps in the same block:
- The raw-Markdown guard added in Universal-Commerce-Protocol#759 compares a case-sensitive suffix,
so a link to page.MD is accepted while page.md is rejected.
- The path is percent-decoded but the fragment is not, so target/#re%61l
reports "Anchor not found" against an id of "real".
Also drops the reassignment of `link` after the site-prefix strip. That
value is never read again -- the last read is the startswith() test above
it -- so the removal is behavior-neutral. Called out here rather than left
silent, since it is not required by the fix.
Adds scripts/test_check_links.py, the first tests for this script, on the
same harness as test_validate_examples.py. 30 tests covering off-site
classification, resolution, the raw-Markdown guard, exit codes, .linkignore
precedence and DOCS_MODE=spec. Six fail on unpatched main.
Wires those tests in the same two places the validator tests run, so they
execute rather than sit unused:
- .github/workflows/docs.yml: a "Run link checker unit tests" step beside
the existing validator step.
- .pre-commit-config.yaml: a check-links-tests hook mirroring
validate-examples-tests.
No behavior change on the current docs. Built the site locally and diffed
findings between this and main: 0 findings either way, in both DOCS_MODE
values. `pre-commit run --all-files` passes.
Ectsang
added a commit
to Ectsang/ucp
that referenced
this pull request
Aug 26, 2026
check_links.py decides whether a link is external by matching its scheme
against ("http", "https") plus a short skip list. Anything else falls
through and its path is resolved against the local build, so an off-site
link is reported as a broken internal one:
//example.com/x -> "Not Found" (protocol-relative)
ftp://example.com/x -> "Not Found" (scheme we do not resolve)
vscode:extension/x -> "Not Found" (scheme carrying no host)
Classify by what the URL carries instead: a host, or a scheme we do not
resolve, means off-site. A relative path containing a colon still parses
with no scheme and no host, so it keeps being resolved.
Two adjacent gaps in the same block:
- The raw-Markdown guard added in Universal-Commerce-Protocol#759 compares a case-sensitive suffix,
so a link to page.MD is accepted while page.md is rejected.
- The path is percent-decoded but the fragment is not, so target/#re%61l
reports "Anchor not found" against an id of "real".
Also drops the reassignment of `link` after the site-prefix strip. That
value is never read again -- the last read is the startswith() test above
it -- so the removal is behavior-neutral. Called out here rather than left
silent, since it is not required by the fix.
Adds scripts/test_check_links.py, the first tests for this script, on the
same harness as test_validate_examples.py, plus a check-links-tests hook
in .pre-commit-config.yaml mirroring validate-examples-tests.
No CI step is added. Editing .github/workflows/docs.yml makes zizmor scan
it, which surfaces pre-existing unpinned-uses findings unrelated to this
change. Left for a maintainer to decide on separately.
No behavior change on the current docs. Built the site locally and diffed
findings between this and main: 0 findings either way, in both DOCS_MODE
values. `pre-commit run --all-files` passes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #723, which moved the specification overview from
overview.mdtooverview/index.mdas part of the vertical documentation hierarchy. Root documentation links were rewritten to the versioned specification but for links containing fragments, the rewriter checked forindex.mdbefore separating the fragment, so links such as:were left pointing at the generated Markdown source instead of the rendered page:
The existing link checker did not catch this because the generated
.mdfile exists andlatest/specificationpaths are otherwise ignored during partial builds.Changes
index.mdand restore them on the canonical rendered URL..mdfiles before applying.linkignorepatterns.Checklist