You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Fixes the keploy/blog-website items from the Oct 2026 SEO/GEO audit.
Titles and H1s
<title> clipping (utils/seo.ts): over-long titles now cut at a phrase or word boundary. A clipped title never ends in a connector (&, and, vs, :, ,) or an unclosed (.
Example: "Integration Testing: Complete Guide (Types, Tools &" becomes "Integration Testing: Complete Guide".
H1 casing (components/post-title.tsx, new utils/titleCase.ts): the old helper lowercased the whole title and then capitalized after every word boundary. The new helper keeps acronyms (API, QA, AI, CI/CD, SDK, JSON, YAML, UI/UX, HTTP, REST, gRPC, E2E, eBPF and others), keeps mixed-case words as written (JavaScript, GraphQL), does not capitalize after an apostrophe, and keeps "vs" lowercase.
Examples: /blog/community/what-is-api-testing, /playwright-api-testing, /qa-testing-tools, /what-is-api-mocking ("A Developer'S Guide To Api Mocking" becomes "A Developer's Guide To API Mocking").
Author E-E-A-T
Empty author box: WordPress (PublishPress) injected an empty author box with the hardcoded box-post-id-16 into every post body. It is now removed by stripPublishPressAuthorBox (lib/author-box.ts). The page's own per-post AuthorCard stays.
Writer bio and avatar: when the post content has no author-box bio, the writer bio and avatar now come from the author's own WordPress user record (Biographical Info and avatar). The record is fetched by exact display-name match in getStaticProps and memoized. It falls back to the old behaviour on any failure.
Updated date: the byline now shows "Updated " when WordPress modified is a later day than date. Both dates use <time datetime>.
The mentions SoftwareApplication reference (@idhttps://keploy.io/#software) now includes applicationCategory: "DeveloperApplication".
Unchanged, already correct: Person author with @id/url/worksFor, and dateModified, which already comes from the WP modified field.
Pre-content clutter
The byline, date, tags, "Summarize with AI" and preferred-source row are now wrapped in <aside aria-label="Post details"> instead of a <div>. Same classes, so the visual layout does not change. Extractors should now treat this as page chrome, so the first content passage is the article lede.
Crawl files
Deleted public/robots.txt (/blog/robots.txt) and its Cache-Control header rule. Crawlers only read /robots.txt at the host root (RFC 9309), so this file was ignored. It also contradicted the root policy on training bots. All rules must live in the root robots.txt (keploy/landing PR). Before merging, make sure the root file has the /blog/api/, /blog/wp/ and /blog/cgi-bin/ disallows (and the /blog/api/proxy-image allow) that this file had.
Sitemap (scripts/generate-sitemap.mjs): <priority> is no longer emitted (Google ignores it). public/sitemap.xml updates on the next Sync Sitemap run.
Skipped and why
Blog llms.txt (Allen Digital, "Feneva", stars): public/llms.txt and public/llms-full.txt here contain none of these strings. They are in the root llms.txt served by keploy/landing, so that is where they need fixing. Live GitHub count is 18,689, so it should read "18K+".
Bulk lastmod: the generator already uses each post's real WP modified date, falling back to date. The shared dates (67 posts on 2026-09-09, 62 on 2026-08-18) come from bulk edits in WordPress itself. Fixing them needs an editorial rule in WordPress, not a code change: avoid bulk re-saves and plugin-wide content rewrites, or use a "substantive update" field.
wp.keploy.io noindex: this is WordPress server config, so it cannot be set from this repo. See manual steps.
Manual steps (WP admin)
On wp.keploy.io, stop the front end from being indexed while keeping /graphql and /wp-content/uploads reachable. Either:
send X-Robots-Tag: noindex from the server or a plugin on all non-/graphql front-end responses, or
Do not use Settings > Reading > "Discourage search engines" alone. It only changes robots meta, and some plugins also apply it to feeds.
Fill in each author's Biographical Info in Users > Profile. The post author card and the Person schema now read it.
Optional: turn off PublishPress's automatic author box ("Show author box after content"). This PR strips it, but it is still dead markup in the WordPress output.
Tests
npm run test:unit: 135/135 pass. Adds tests/lib/titleCase.test.ts, which covers headline case, phrase-boundary clipping, the author-box strip and the exact-name WP user match.
npx tsc --noEmit: pass.
npm run lint: 0 errors (only the existing exhaustive-deps warnings).
npm run build: not verified locally. It failed inside next/font (Google DM_Sans in pages/_app.tsx, a file this PR doesn't touch) with "Cannot read properties of null", on Node 26.3. That looks like a local Node/Next mismatch, so please rely on the Vercel preview build.
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Title-formatting issues remain unresolved, and root crawl-rule coordination and a successful preview build still need confirmation.
No open issues 🎉
2 previously missed issues
In code that hasn't changed since last review
Preserve UI/UX acronym casing in headline conversion
utils/titleCase.ts:12
The canonical forms include CI/CD but omit UI/UX. Because the helper splits tokens only on whitespace, toHeadlineCase("ui/ux testing") returns Ui/ux Testing rather than UI/UX Testing. Add UI/UX to the lookup so this acronym receives the same normalization as CI/CD, and cover the lowercase input in the acronym test.
Handle punctuation when preserving VS Code casing
utils/titleCase.ts:34
The lookahead compares the entire next whitespace-delimited token, so punctuation defeats the VS Code exception. Debugging in VS Code: A Guide becomes Debugging In vs Code: A Guide, and Debugging in (VS Code) becomes Debugging In (vs Code), lowercasing an already-correct product name in the H1. Strip the next token's surrounding punctuation before comparing it with code, and add regression cases for the colon and closing parenthesis.
…itle clip and acronym fixes
- Select exact-name WP writer in getStaticProps and pass only name/avatar/bio
(toPublicAuthor); drop email from the users query entirely
- stripPublishPressAuthorBox matches each begin/end code block separately and
removes only PublishPress ones
- clipTitleAtBoundary keeps C++ (only strips standalone '+'), trims before the
first unmatched '(' incl. nested and position 0
- titleCase acronym lookup uses a Map (no prototype keys)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Neha Gupta <gneha21@yahoo.in>
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
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.
Fixes the
keploy/blog-websiteitems from the Oct 2026 SEO/GEO audit.Titles and H1s
<title>clipping (utils/seo.ts): over-long titles now cut at a phrase or word boundary. A clipped title never ends in a connector (&,and,vs,:,,) or an unclosed(.Example: "Integration Testing: Complete Guide (Types, Tools &" becomes "Integration Testing: Complete Guide".
components/post-title.tsx, newutils/titleCase.ts): the old helper lowercased the whole title and then capitalized after every word boundary. The new helper keeps acronyms (API, QA, AI, CI/CD, SDK, JSON, YAML, UI/UX, HTTP, REST, gRPC, E2E, eBPF and others), keeps mixed-case words as written (JavaScript, GraphQL), does not capitalize after an apostrophe, and keeps "vs" lowercase.Examples: /blog/community/what-is-api-testing, /playwright-api-testing, /qa-testing-tools, /what-is-api-mocking ("A Developer'S Guide To Api Mocking" becomes "A Developer's Guide To API Mocking").
Author E-E-A-T
box-post-id-16into every post body. It is now removed bystripPublishPressAuthorBox(lib/author-box.ts). The page's own per-post AuthorCard stays.getStaticPropsand memoized. It falls back to the old behaviour on any failure.modifiedis a later day thandate. Both dates use<time datetime>.Schema
publisher(@idhttps://keploy.io/#organization) now carriesurlandsameAs.sameAsincludes https://www.wikidata.org/wiki/Q137395272, which was also added to the global OrganizationsameAs.mentionsSoftwareApplication reference (@idhttps://keploy.io/#software) now includesapplicationCategory: "DeveloperApplication".@id/url/worksFor, anddateModified, which already comes from the WPmodifiedfield.Pre-content clutter
<aside aria-label="Post details">instead of a<div>. Same classes, so the visual layout does not change. Extractors should now treat this as page chrome, so the first content passage is the article lede.Crawl files
public/robots.txt(/blog/robots.txt) and its Cache-Control header rule. Crawlers only read/robots.txtat the host root (RFC 9309), so this file was ignored. It also contradicted the root policy on training bots. All rules must live in the root robots.txt (keploy/landing PR). Before merging, make sure the root file has the/blog/api/,/blog/wp/and/blog/cgi-bin/disallows (and the/blog/api/proxy-imageallow) that this file had.scripts/generate-sitemap.mjs):<priority>is no longer emitted (Google ignores it).public/sitemap.xmlupdates on the next Sync Sitemap run.Skipped and why
public/llms.txtandpublic/llms-full.txthere contain none of these strings. They are in the root llms.txt served by keploy/landing, so that is where they need fixing. Live GitHub count is 18,689, so it should read "18K+".modifieddate, falling back todate. The shared dates (67 posts on 2026-09-09, 62 on 2026-08-18) come from bulk edits in WordPress itself. Fixing them needs an editorial rule in WordPress, not a code change: avoid bulk re-saves and plugin-wide content rewrites, or use a "substantive update" field.Manual steps (WP admin)
On wp.keploy.io, stop the front end from being indexed while keeping
/graphqland/wp-content/uploadsreachable. Either:X-Robots-Tag: noindexfrom the server or a plugin on all non-/graphqlfront-end responses, or/?p=*and post permalinks) to https://keploy.io/blog/....Do not use Settings > Reading > "Discourage search engines" alone. It only changes robots meta, and some plugins also apply it to feeds.
Fill in each author's Biographical Info in Users > Profile. The post author card and the Person schema now read it.
Optional: turn off PublishPress's automatic author box ("Show author box after content"). This PR strips it, but it is still dead markup in the WordPress output.
Tests
npm run test:unit: 135/135 pass. Addstests/lib/titleCase.test.ts, which covers headline case, phrase-boundary clipping, the author-box strip and the exact-name WP user match.npx tsc --noEmit: pass.npm run lint: 0 errors (only the existingexhaustive-depswarnings).npm run build: not verified locally. It failed insidenext/font(Google DM_Sans inpages/_app.tsx, a file this PR doesn't touch) with "Cannot read properties of null", on Node 26.3. That looks like a local Node/Next mismatch, so please rely on the Vercel preview build.🤖 Generated with Claude Code