Skip to content

feat(docs): site health + AI-citation fixes (A1, A2, A8 …) - #897

Open
dhananjay6561 wants to merge 16 commits into
keploy:mainfrom
dhananjay6561:feat/ai-citation-health
Open

feat(docs): site health + AI-citation fixes (A1, A2, A8 …)#897
dhananjay6561 wants to merge 16 commits into
keploy:mainfrom
dhananjay6561:feat/ai-citation-health

Conversation

@dhananjay6561

@dhananjay6561 dhananjay6561 commented Aug 7, 2026

Copy link
Copy Markdown
Member

Summary

Site Health + AI-citation / GEO fixes for the docs site, from the Aug 2026 SEMrush audit (Site Health 84%, AI Search Health 84%) and the schema-type audit. Covers the docs tickets A1–A10 / AI1–AI6 plus a full structured-data overhaul from the schema audit, prioritising template-level fixes that clear hundreds of pages at once.

Scope: docs repo only. Off main, independent of the Core Web Vitals PR (#896). All work verified against the served version (v4.0.0) on current main.

Note on the audit vs. live docs: the SEMrush export ran on an older main. A number of flagged pages (5 *-filter quickstarts, samples-mux/mysql/nextjs, agile-unit-testing, etc.) no longer exist in version-4.0.0, and several "thin" glossary pages are now 1,300–2,400 words. Rows below reflect the live state, not the stale export.


📊 Ticket status

ID Area What Status
A1 / AI1 SEO AI image added to Article schema — fixes ~146 "invalid structured data" ✅ Done
A2 SEO <title> differentiated from on-page <h1> — fixes 124 duplicates ✅ Done
A7 SEO Unique meta descriptions ✅ Done (every surviving flagged page has one; 4 no longer exist)
A8 A11Y AI SEO Descriptive alt on 13 images / 8 pages; samples-echo triple-h1 → single h1 ✅ Done
A9 SEO http://https:// doc links (configuration-file); auth-setup title-too-short + dup description ✅ Done
Schema: specialize SEO AI Generic ArticleTechArticle + APIReference (0 generic Article remain) ✅ Done
Schema: @graph SEO AI Consolidate the ~6 fragmented Organization/WebSite/SoftwareApplication nodes under one @id graph ✅ Done
Schema: DefinedTerm SEO AI DefinedTerm on 37 glossary term pages, keyed to the hub's DefinedTermSet @id ✅ Done
Schema: bespoke pages SEO AI / (CollectionPage+ItemList), /about (AboutPage), /leadership (CollectionPage+ItemList+Person), /security (WebPage+ContactPoint on shared Org @id), /privacy-policy (DigitalDocument) ✅ Done
Schema: CI guard SEO scripts/verify-schema-graph.js — fails on invalid JSON-LD or dangling @id ✅ Done
AI4: FAQPage AI FAQPage JSON-LD (Question/Answer) on the 3 FAQ docs ✅ Done
A3 / AI2 A11Y AI icon-link aria-labels ✅ Already satisfied in theme
A10 SEO Legacy /docs/1.0.0/* + 2.0.0 are noIndex: true in config — intentional ✅ Verified, no action
Schema: net-new (VideoObject / Course / Event / Offer / SoftwareSourceCode / wider HowTo) SEO AI Targets are latent / noIndex / commented components (ResponsivePlayer→v1.0.0 noindex, Product.js latent) with no rich-result payoff 🚫 Recommend skip (schema noise, no benefit)
A4 / AI5 SEO AI Cross-linking pages with a single internal link 📝 Content follow-up (hand-authored, not sprayed)
AI4: wider HowTo AI Adopt <HowTo> in more step-by-step guides 📝 Content follow-up
A5 / AI3 SEO AI Expand thin pages 📝 Reduced to 2 pages (integrations-pr-agent, windows-wsl); the flagged glossary pages are already 1.3k–2.4k words; the flagged filter pages were deleted
A6 PERF brotli/gzip ⚙️ Vercel/CDN host setting, not source

✅ Changes

Structured data — one @id-linked entity graph

  • Every route emitted several disconnected Organization nodes (site-wide + SoftwareApplication.publisher + Article.author + Article.publisher), which had drifted to two different logo URLs. Each site-wide entity (Organization, WebSite, SoftwareApplication) is now declared once in src/schema/siteEntities.js with a stable @id and referenced by @id everywhere, so consumers merge them into a single entity per page.
  • docusaurus.config.js: three sibling ld+json blocks → one @graph (siteGraph).
  • DocItem: author/publisher/isPartOf reference the shared @ids; the Article @id is derived from the trailing-slash canonical URL so it matches the URLs the glossary hub and bespoke pages emit.
  • DefinedTerm on the 37 glossary term pages, keyed to the hub's DefinedTermSet @id, so the hub listing and the term page resolve to one entity (latest version only).
  • scripts/verify-schema-graph.js walks the built HTML and fails CI on invalid JSON-LD or a dangling @id.

Structured data — schema specialization

  • DocItem routes API-reference pages to APIReference and all other docs to TechArticle (both Article subtypes, same properties — just a precise type). 0 generic Article remain.
  • A1 · image on the Article schema — DocItem computed socialImage but never emitted it, so ~146 pages were "invalid structured data". Now included with the site-wide 1200×630 og:image as fallback.

Structured data — bespoke React pages

  • / (index.js): generic ArticleCollectionPage + ItemList built from GetStartedPaths' exported entry-point data (DocItem already suppresses Article on /docs/ for the same reason).
  • /about: AboutPage (standing info about the docs, no author/date for an Article).
  • /leadership: CollectionPage + ItemList of Person (photo + Twitter + worksFor → shared Org @id).
  • /security: WebPage + Organization contactPoint attached to the shared Org @id (responsible-disclosure email).
  • /privacy-policy: DigitalDocument with dateModified.
  • GlossaryCard now uses Docusaurus Link (build-checked under onBrokenLinks: throw, trailingSlash-aware).

AI / SEO

  • FAQPage (AI4) — dependency-free remark plugin (src/remark/remarkFaqSchema.js) extracts ### question + answer prose into Question/acceptedAnswer pairs and injects FAQPage JSON-LD on the 3 FAQ docs.
  • A2 · title ≠ h1 — suffixed | Keploy Docs on 124 pages.
  • A7 · unique meta descriptions — glossary/general pages, share-tests, k8s-proxy.
  • A8alt on 13 images / 8 pages; samples-echo had two body # sections producing three h1s → demoted to ##.
  • A9 — four http://keploy.io/docs/... doc links in configuration-filehttps:// (regex examples in code blocks left as-is); api-testing-auth-setup got a descriptive title (was "Auth Setup", too short) and a unique description.

🧭 Notes / deviations from the plan

  • Net-new schema emitters (VideoObject / Course / Event / Offer / SoftwareSourceCode / wider ItemList): recommended skip. Their targets are latent, commented-out, or noIndex components (e.g. ResponsivePlayer only renders on the noIndex v1.0.0 page; Product.js is not on an indexed route). Emitting them would add schema with no rich-result or citation payoff.
  • Review/rating schema: out of scopeAggregateRating/Review for one's own product violates Google's structured-data policy (manual-action risk). Real ratings stay surfaced via the Organization sameAs links (G2/Gartner/Capterra). (Same call as CWV perf(docs): core web vitals — self-host fonts, drop dead assets, defer pixel, add security headers #896.)
  • A3 already satisfied — footer socials, heading hash-links, and the breadcrumb home link already carry aria-labels.
  • A4 / A5 / AI4 (content): left as follow-ups — cross-linking and content expansion are hand-authored in meaningful batches, not machine-sprayed. A5 in particular has shrunk to 2 genuinely thin pages (the flagged glossary pages are already substantial; the flagged filter pages were deleted).
  • A6 is a Vercel/CDN host setting, not a source change — flagged for ops.

✅ Verification

  • npm run build succeeds (onBrokenLinks: throw); all changed files pass prettier 3.9.6.
  • node scripts/verify-schema-graph.js build on the served versions (skips the noIndex 1.0.0/2.0.0): 479 pages, 972 JSON-LD blocks, 0 invalid JSON, 0 dangling @id. Top @types: Organization/WebSite/SoftwareApplication (one each per page via the shared graph), BreadcrumbList 208, TechArticle 174, DefinedTerm 37, HowTo 34, APIReference 31, FAQPage 3, CollectionPage 2, AboutPage/DigitalDocument/WebPage/DefinedTermSet 1 each — 0 generic Article. (All-versions counts are higher because 1.0.0/2.0.0 carry their own copies.)
  • Recommend validating a doc page, the home page, and a FAQ page in the Rich Results Test, then re-running the SEMrush Site Audit + AI Search Health to confirm the plan's DoD (Site Health ≥ 90%, AI ≥ 92%).

DocItem computed socialImage but never included it in the Article/
TechArticle JSON-LD, so every doc page emitted an Article without the
required 'image' field -> ~146 pages flagged invalid structured data.

Add image to articleSchema, falling back to the site-wide default social
card (the same 1200x630 og:image from docusaurus.config.js) when a doc
sets no front-matter image.

Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com>
The swizzled DocItem set <title>{title}</title>, identical to the on-page
<h1> (also the doc title) -> SEMrush 'Duplicate content in h1 and title'
on 124 pages. Append ' | Keploy Docs' so the title differs from the h1.

Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com>
Copilot AI lite review requested due to automatic review settings August 7, 2026 10:00
13 images across 8 doc pages (glossary GIFs + running-keploy screenshots)
had no alt attribute -> SEMrush 'Missing ALT' + weaker AI/accessibility
extraction. Add descriptive alt to each. utg-vscode-extension from the
audit list no longer exists on main, so it is not included.

Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com>
Pre-existing whitespace/list-indent/final-newline issues in these two
files (unrelated to the alt fix) would fail the prettier CI check once the
files are touched. Apply prettier so the PR is clean.

Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Updates the Docusaurus DocItem theme component to address SEO / AI-citation audit findings by improving structured data completeness and avoiding duplicate <title>/<h1> signals across docs pages.

Changes:

  • Adds an image field to the per-page Article/TechArticle JSON-LD (with a fallback when front matter has no image).
  • Appends a suffix to <title> to differentiate it from the on-page <h1>.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/theme/DocItem/index.js Outdated
Comment thread src/theme/DocItem/index.js
Wrap the long articleImage fallback line and flatten the pre-existing
nested-ternary indentation so the swizzled DocItem is clean under the
CI-pinned prettier 2.8.8 (--check runs on changed files).

Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com>
The prettify_code workflow pins prettier_version 2.8.8, but creyD/
prettier_action's npx step actually installs and runs prettier 3.9.6.
3.x changed nested-ternary indentation, so the prior 2.8.8-flattened
schemaType ternary failed CI. Reformat to 3.9.6 (the version CI runs) so
--check passes; all other changed files are already 3.9.6-clean.

Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com>
Every /docs page emitted a generic Article. Route API-reference pages
(api-testing-*, public-api, cli-commands, sdk-installation — by tag or
path) to APIReference, and default all other docs to TechArticle. Both are
Article subtypes accepting the same properties as articleSchema, so the
schema shape is unchanged — just a precise, AI/Google-friendly type.
Built HTML: 35 APIReference, 343 TechArticle, 0 generic Article from docs.

Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com>
general-glossary + 3 glossary terms shared one boilerplate description;
share-tests copied the generate-api-tests description; k8s-proxy had a
wrong (NextJS/Drizzle) description. Give each a unique, accurate one to
clear SEMrush 'Duplicate meta descriptions'.

Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com>
Pre-existing leading-space paragraphs (unrelated to the description
change) would fail the prettier CI check once the file is touched.

Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com>
/leadership, /security and /privacy-policy emitted zero page-level schema.
Add JSON-LD: leadership -> CollectionPage + ItemList of Person (name,
photo, Twitter, worksFor Keploy); security -> WebPage + security
ContactPoint (responsible-disclosure email); privacy-policy ->
DigitalDocument with dateModified. Verified in built HTML.

Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com>
@dhananjay6561 dhananjay6561 self-assigned this Aug 7, 2026
New dependency-free remark plugin extracts each ### question + following
answer prose from FAQ docs (keploy-explained/*-faq) and injects FAQPage
JSON-LD with Question/acceptedAnswer pairs, so they're eligible for FAQ
rich results and AI extraction. Built HTML: FAQPage with 14/11/12 Q&A on
the three FAQ pages.

Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com>
Every route emitted several disconnected Organization nodes: the site-wide
one plus copies inlined as SoftwareApplication.publisher, Article.author and
Article.publisher, which had drifted to two different logo URLs. Declare each
site-wide entity (Organization, WebSite, SoftwareApplication) once in
src/schema/siteEntities.js with a stable @id and reference it by @id
everywhere else, so consumers merge them into a single entity per page.

- docusaurus.config.js: three sibling ld+json blocks -> one @graph (siteGraph)
- DocItem: author/publisher/isPartOf reference the shared @ids; derive the
  Article @id from the trailing-slash canonical URL so it matches the URLs the
  glossary hub and bespoke pages emit; add a DefinedTerm on glossary term
  pages, keyed to the hub's DefinedTermSet @id (latest version only)
- bespoke pages: about -> AboutPage, index -> CollectionPage + ItemList
  (off GetStartedPaths' exported data), leadership -> CollectionPage of Person,
  privacy-policy -> DigitalDocument, security -> WebPage + Organization
  contactPoint on the shared Org @id
- glossary hub DefinedTerms gain the same @id as their term pages so the two
  listings resolve to one entity
- GlossaryCard uses Docusaurus Link (build-checked, trailingSlash-aware)
- scripts/verify-schema-graph.js: fail CI on invalid JSON-LD or dangling @id

Verified: clean build, verify-schema-graph reports 0 invalid / 0 dangling
across 479 pages / 972 blocks; no generic Article remains.

Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com>
- samples-echo: two body sections used a single `#`, producing three h1s on
  the page (title + two). Demote both to `##` so the page has one h1 and a
  clean heading hierarchy (A8, multiple-h1).
- configuration-file: rewrite four in-body doc links from http:// to https://
  (the two remaining http:// occurrences are regex examples in a code block
  and are left as-is) (A9, HTTP-link-on-HTTPS-site).
- api-testing-auth-setup: the title "Auth Setup" was flagged as too short and
  its description was a near-duplicate stub; give it a descriptive title and a
  unique description (sidebar_label stays "Auth Setup") (A9, title-too-short).

Verified: clean build (onBrokenLinks: throw), verify-schema-graph 0 invalid /
0 dangling.

Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com>
…inks

integrations-pr-agent was ~120 words (flagged "low word count"). Add a
"What the PR Agent does" section and a Related block linking the AI test
generator, UTG PR agent, and CI/CD GitHub setup, so the page carries real
content and more than one internal link (A4).

Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com>
…d links

windows-wsl was ~177 words. Add why-WSL context, a Prerequisites section,
clearer install steps, and a Related block (macOS/Linux install, CLI
reference, common errors) to fix the thin-content and single-internal-link
flags (A5/A4).

Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com>
Glossary term pages each had only one internal link (SEMrush "pages with
only one internal link"). Add a "Related Terms" section to every term page
linking 3–4 conceptually related terms plus the glossary hub, so terms form
a connected cluster that both readers and AI engines can traverse.

Link targets use each term's real route slug (e.g. agile-testing.md serves
at /agile-unit-testing/, grey-box-testing.md at /gray-box-testing/), verified
by a clean build under onBrokenLinks: throw.

Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com>
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