Skip to content

[quality] sitemap.xml advertises 5 internal agent-skill docs and /search to search engines #382

Closed

Description

@hivecommons-hive

Finding

docs/ is served with routeBasePath: '/' (docusaurus.config.js:95), so every
docs/**/*.md becomes a public route
. docs/skills/ holds five documents that
are not site content — they are authoring instructions for agents working on this
repository:

docs/skills/blog-management.md
docs/skills/doc-authoring.md
docs/skills/manifest.md
docs/skills/skill-improvement.md
docs/skills/ui-standards.md

docs/skills/manifest.md:1-3 says so in its own words:

Agent Skills Manifest

This manifest indexes specialized skills for agents working on this repository.
Load these skills as needed to ensure compliance with repository standards.

All five are built, published, and submitted to search engines. Verified at
00b44df, node v26.8.2, 2026-09-21:

$ SITE_URL=https://endusers.cncf.io BASE_URL=/ npm run build:production
[SUCCESS] Generated static files in "build".

$ grep -c '<loc>' build/sitemap.xml
43
$ grep -o '<loc>[^<]*</loc>' build/sitemap.xml | grep -E '/skills/|/search'
<loc>https://endusers.cncf.io/search</loc>
<loc>https://endusers.cncf.io/skills/blog-management</loc>
<loc>https://endusers.cncf.io/skills/doc-authoring</loc>
<loc>https://endusers.cncf.io/skills/manifest</loc>
<loc>https://endusers.cncf.io/skills/skill-improvement</loc>
<loc>https://endusers.cncf.io/skills/ui-standards</loc>

Six of the 43 advertised URLs should not be advertised.

What a searcher is offered

build/skills/ui-standards/index.html ships with the site's own branding and an
agent trigger phrase as its public SEO description:

<title>Frontend UI Standards | CNCF End User Community</title>
<meta name=description content="Use when adding or changing Docusaurus components,
global CSS, responsive layouts, accessibility, or theme-aware UI.">

"Use when ..." is skill-dispatch prose written for a tool, and it is what Google
will print under a cncf result. None of the five carries any robots meta, and
no navbar item or sidebar entry links any of them (sidebars.js autogenerates
only architectures and community), so they are orphan pages whose sole route
to a reader is the sitemap this repository hands to crawlers.

/search is a second, separate leak

The search page does try to opt out, and the opt-out does not work. The chain is
three files deep and each link is verifiable in node_modules:

  1. docusaurus-plugin-search-local/lib/client/theme/SearchPage/SearchPage.jsx:69-72
    emits the tag with the wrong attribute:

    /* We should not index search pages
       See https://github.com/facebook/docusaurus/pull/3233 */
    <meta property="robots" content="noindex, follow"/>
  2. @docusaurus/core/lib/client/serverHelmetUtils.js:12-21 decides noIndex with
    isNoIndexTag, which returns false unless tag.name is robots:

    if (!tag.name || !tag.content) { return false; }
    return tag.name.toLowerCase() === 'robots' && ...
  3. @docusaurus/plugin-sitemap/lib/createSitemap.js:18-25 filters a route only when
    isNoIndexMetaRoute is true, which reads that same noIndex flag.

property="robots" is not a name, so noIndex stays false, the route is never
filtered, and /search is submitted for indexing. Crawlers ignore
property="robots" for exactly the same reason, so there is no second line of
defence. An indexed internal search page is thin duplicate content over the site's
own pages — the case Docusaurus PR #3233, cited in the comment above, exists to
prevent.

Why existing gates miss it

Recommendation

One PR closes both halves; both edits are mechanical and land in the same change set.

  • Exclude the two route families from the sitemap. In docusaurus.config.js,
    inside the classic preset options (sibling of docs, blog, theme), add:
        sitemap: {
          // /search is thin duplicate content; the plugin's own opt-out is
          // emitted as property="robots", which neither Docusaurus nor a
          // crawler honours. /skills/* is agent tooling, not site content.
          ignorePatterns: ['/search', '/skills/**'],
        },
  • Stop publishing the skill docs as reader-facing pages. Add to the
    frontmatter of each of the five files in docs/skills/:
unlisted: true
  `unlisted: true` keeps the page reachable by direct URL for contributors while
  removing it from the sitemap, the local search index, and adding a real
  `<meta name="robots" content="noindex, nofollow">`. Prefer it to `draft: true`
  (which drops the pages from the production build entirely) only if someone
  still links these from a PR; `docs/skills/manifest.md` is referenced from
  `ROADMAP.md:52`, so a reachable URL is worth keeping.

Verify after the change:

SITE_URL=https://endusers.cncf.io BASE_URL=/ npm run build:production
grep -o '<loc>[^<]*</loc>' build/sitemap.xml | grep -E '/skills/|/search'   # expect no output
grep -c '<loc>' build/sitemap.xml                                          # expect 37

Why there is no PR attached

Both deliverables are production changes — site configuration and content
frontmatter. The quality lane is restricted to test-only PRs, so this needs a human
or another lane to land. The exact replacement text above is given verbatim so
applying it is mechanical rather than a judgement call.

A test-only guard is not a substitute and is not being proposed alongside: any
assertion that /skills/** stays out of the sitemap is red against main until the
first box is ticked, and the route-derivation machinery such a test would need is
already claimed by open PR #275.

Evidence

  • Revision 00b44df, node v26.8.2, @docusaurus/plugin-sitemap 3.10.2, run locally 2026-09-21.
  • Build: SITE_URL=https://endusers.cncf.io BASE_URL=/ npm run build:production -> [SUCCESS].
  • Sitemap: 43 <loc> entries, 6 of them /search + /skills/* (listed above).
  • Robots meta: build/skills/ui-standards/index.html has none;
    build/search/index.html has <meta property=robots content="noindex, follow"/>.
  • Unit coverage: npm run test:unit -- --experimental-test-coverage -> 55/55 pass;
    no test reads sitemap config or the published route set.
  • End-to-end coverage: unobtainable rather than absent — the repository declares no
    playwright, cypress or puppeteer, so there is no e2e suite to consult. See
    [quality] CI publishes no coverage evidence, so coverage findings cannot be verified #186 for the missing coverage-evidence pipeline.

Priority

  • Impact: high — five internal tooling documents are advertised to search engines under CNCF branding on a pre-launch site, and an internal search page is submitted as thin duplicate content
  • Effort: low — one config block plus five frontmatter lines, no new dependency

Filed by quality agent (hold-gated mode)

— hive: agent=quality backend=copilot model=claude-opus-5

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent/qualityApproved by a Hive merger/owner for auto-merge on green CIbugSomething isn't workingqualityApproved by a Hive merger/owner for auto-merge on green CI

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions