From cqa-tools
Validates links in modular documentation repos: broken xrefs/includes/images, duplicate IDs, redirects, and interlinking for CQA P15-P17, Q24-Q25.
npx claudepluginhub redhat-documentation/redhat-docs-agent-tools --plugin cqa-toolsThis skill is limited to using the following tools:
| # | Parameter | Level |
Validates Markdown documentation links, detecting broken relative links, missing anchor targets, malformed URLs, and orphaned documentation files. Use before releases or doc updates.
Assesses documentation user focus for CQA Q6-Q11: persona targeting (admin/developer), pain points, acronym expansion, additional resources, admonition density, and intro audience matching.
Validates documentation references including file paths, links, line numbers, and code examples using Read, Grep, Glob. Ensures docs accuracy across projects.
Share bugs, ideas, or general feedback.
| # | Parameter | Level |
|---|---|---|
| P15 | No broken links (xrefs, includes, images) | Required |
| P16 | Redirects work correctly | Required |
| P17 | Content interlinked within 3 clicks of domain home | Important |
| Q24 | Content includes links to relevant content journey | Important |
| Q25 | Pages interlinked within 3 clicks | Important |
Some repos use modules/ instead of topics/ for content files. All topics/ references in this skill apply equally to modules/. The automation scripts accept --scan-dirs to override the default scan directories.
Ask the user for the path to their Red Hat modular documentation repository. Store as DOCS_REPO.
Every cross-reference, include directive, and image reference must resolve to an existing target. No duplicate IDs are allowed.
Run the reference validation script:
python3 ${CLAUDE_PLUGIN_ROOT}/skills/cqa-assess/scripts/validate-refs.py "$DOCS_REPO"
This checks 4 things:
xref:TARGET[] must match a declared [id="..."] (supports both _{context} and plain IDs, and file-based xrefs like xref:file.adoc#anchor[])include::path[] must resolve to an existing file on diskimage::path[] must resolve to a file under the :imagesdir: directory (auto-discovered from common/attributes.adoc, defaults to images)[id="..."] anchor| Cause | Example | Fix |
|---|---|---|
| File renamed without updating xrefs | xref:old-name_{context}[] → file now has [id="new-name_{context}"] | Update xref to match actual ID |
| File deleted without removing xrefs/includes | xref:deleted-topic_{context}[] | Remove the xref or redirect to replacement content |
| ID in file doesn't match filename prefix | File proc_foo.adoc has [id="foo_{context}"] (no proc_ prefix) | Xref must match declared ID, not filename |
| Typo in image path | image::architectre/diagram.png[] | Fix spelling to match actual path |
| Score | Criteria |
|---|---|
| 4 | 0 broken xrefs, 0 missing includes, 0 missing images, 0 duplicate IDs |
| 3 | 1-3 broken references (minor oversights) |
| 2 | Multiple broken references across different files |
| 1 | Widespread broken references or not checked |
When content is renamed, moved, or restructured, old URLs must continue to resolve. The redirect mechanism depends on the publishing platform.
_redirects, redirect maps, or platform-specific redirect config[id="..."] anchors against any published version to find renamed/deleted pages| Platform | Redirect mechanism | Repo responsibility |
|---|---|---|
| Pantheon (ccutil) | Managed at CCS publishing infrastructure level | Repo maintains correct IDs; redirect requests filed with CCS publishing team during stage branch process |
| GitLab Pages | _redirects file in repo root | Repo must maintain redirect file |
| Antora | page-aliases attribute in page headers | Repo must set aliases |
| Score | Criteria |
|---|---|
| 4 | Redirects managed at platform level OR redirect configuration in place and verified; no Antora module prefixes in links; cross-guide links use link: not xref: |
| 3 | Minor redirect gaps (1-2 recently renamed pages without redirects) |
| 2 | Multiple broken URLs from recent restructuring with no redirect plan |
| 1 | No redirect strategy or widespread broken published URLs |
Content should be interlinked so that users can navigate between related topics. Every topic should be reachable within 3 clicks from the guide's domain home page. The content journey must guide users through related content — concepts link to procedures, procedures link to related concepts and other procedures, and cross-guide links connect admin and user workflows.
titles/administration_guide/master.adoc, titles/user_guide/master.adoc) to understand the full guide structure{prod-ug-url} in admin guide topics and assemblies{prod-ag-url} in user guide topics and assemblieslink: not xref: (guides are built separately)link: macros must have descriptive link text (not empty [])administration-guide:, user-guide:)[id="..."] declared in the target guide's files. Legacy anchor names from the pre-modularization era may not resolve in the current format.link: macros must NOT use _{context} in the anchor fragment. The {context} attribute resolves to the source guide's context value, not the target guide's. For example, link:{prod-ag-url}some-id_{context}[...] in a user guide file resolves {context} to user_guide, producing the anchor some-id_user_guide — but the actual anchor in the admin guide HTML is some-id_administration_guide. The anchor must hardcode the target guide's context string directly.Cross-guide anchors are not validated by validate-refs.py (cross-guide link: macros are external URLs, not xref: targets). These must be checked manually:
Find {context} in cross-guide anchors — these are always wrong:
# Links to admin guide with {context} in anchor (in user guide files)
grep -rn 'link:{prod-ag-url}.*_{context}' topics/user_guide/ assemblies/user_guide/ --include='*.adoc'
# Links to user guide with {context} in anchor (in admin guide files)
grep -rn 'link:{prod-ug-url}.*_{context}' topics/administration_guide/ assemblies/administration_guide/ --include='*.adoc'
Any matches are broken. Replace _{context} with the target guide's hardcoded context string (_administration_guide or _user_guide).
Verify all cross-guide anchors match declared IDs — for each link:{prod-ag-url}ANCHOR[...] or link:{prod-ug-url}ANCHOR[...]:
[)[id="ANCHOR"] in the target guide's files (with _{context} appended to the declared ID pattern)_ + target guide's context valueContext values — determined by the :context: attribute set in each guide's master.adoc:
:context: administration_guide → anchors end with _administration_guide:context: user_guide → anchors end with _user_guide| Issue | Example | Fix |
|---|---|---|
{context} in cross-guide anchor | link:{prod-ag-url}some-id_{context}[...] in user guide | Replace _{context} with _administration_guide (admin targets) or _user_guide (user targets) |
Empty link text on link: macro | link:{prod-ag-url}target-id[] | Add descriptive text: link:{prod-ag-url}target-id[Configuring feature] |
| Legacy target ID | link:{prod-ug-url}old-section-name[...] | Update to match current [id="..."] in target file |
| Non-existent target | link:{prod-ug-url}removed-content[...] | Remove link or redirect to replacement content |
| Antora module prefix | link:{prod-ag-url}administration-guide:section[...] | Remove administration-guide: prefix |
xref: across guides | xref:admin-topic_{context}[] in user guide | Change to link:{prod-ag-url}admin-topic_{context}[...] |
| Score | Criteria |
|---|---|
| 4 | All topics reachable within 3 clicks; concepts link to procedures and vice versa; cross-guide links where relevant; all cross-guide links have descriptive text and valid targets |
| 3 | Most topics interlinked; a few isolated topics without cross-references; minor cross-guide link issues (≤3 empty link texts or legacy targets) |
| 2 | Significant sections with no cross-references to related content; multiple broken cross-guide links |
| 1 | Content is siloed with minimal interlinking; no cross-guide links; broken navigation paths |
All external links must be current and use version attributes where applicable. OpenShift documentation URLs must use {ocp4-ver} attribute instead of hardcoded version numbers or /latest/. Hardcoded versions that describe feature availability thresholds (e.g., "available on OpenShift 4.20 and later") are acceptable.
container-platform/X.Y/ where X.Y is NOT {ocp4-ver}. These should use the attribute./latest/ in OCP URLs — Search for container-platform/latest/. These should use {ocp4-ver} for consistent version pinning.common/attributes.adoc must also use {ocp4-ver}, not /latest/ or hardcoded versions.devfile.io/docs/ should use a consistent version across all files.# Find hardcoded OCP versions in links
grep -rn 'container-platform/[0-9]' topics/ modules/ assemblies/ --include='*.adoc' 2>/dev/null
# Find /latest/ links
grep -rn 'container-platform/latest/' topics/ modules/ assemblies/ common/ --include='*.adoc' 2>/dev/null
| Score | Criteria |
|---|---|
| 4 | All OCP links use {ocp4-ver} attribute. No /latest/ in active content. Link attributes in attributes.adoc use {ocp4-ver}. |
| 3 | 1-5 instances of /latest/ or hardcoded versions. Attribute links updated. |
| 2 | Widespread hardcoded versions or /latest/ usage (>5 instances). Inconsistent. |
| 1 | No version management strategy. Hardcoded versions throughout. |
After fixing any violations, re-run the reference validation:
python3 ${CLAUDE_PLUGIN_ROOT}/skills/cqa-assess/scripts/validate-refs.py "$DOCS_REPO"
Then run Vale to ensure no new warnings were introduced:
# Adjust directory names to match your repo structure (topics/ or modules/)
vale assemblies/ topics/ titles/administration_guide/master.adoc titles/user_guide/master.adoc