Skip to content

Fix localized SEO metadata from Ahrefs audit#7888

Open
lawrencecchen wants to merge 26 commits into
mainfrom
feat-seo-site-audit
Open

Fix localized SEO metadata from Ahrefs audit#7888
lawrencecchen wants to merge 26 commits into
mainfrom
feat-seo-site-audit

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Fixes the actionable metadata findings in the cmux Ahrefs crawl.

  • bounds descriptions to 110–160 characters where language structure supports it, with localized product context for short copy
  • bounds flagged page titles and adds localized context to short standalone titles
  • adds canonical Open Graph URLs and complete social cards to pricing, legal, and oh-my-pi pages
  • preserves concise CJK metadata where raw character-count thresholds are not meaningful

Validation:

  • bun test tests/seo.test.ts
  • bun run typecheck
  • focused bun run lint
  • bun run build
  • rendered representative English and localized routes locally and verified title, description, and og:url output

View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.


Note

Medium Risk
Changes sit on global SEO helpers, locale middleware, and canonical/hreflang behavior for high-traffic routes; regressions could affect search snippets or wrong-locale URLs, though coverage in tests/seo.test.ts is extensive.

Overview
Addresses Ahrefs audit metadata issues by replacing ad hoc seoDescription usage on key routes with i18n/audited-seo.ts helpers that pick bounded, locale-aware titles and descriptions (Unicode-safe lengths, contextual fallbacks, FAQ/route prose for compare pages). seo.ts gains seoTitle, richer description suffixes, sentence joining, and stricter candidate filtering.

Landing, blog, compare, pricing, home layout, and Oh My Pi generateMetadata now use those helpers; several pages use title: { absolute: ... }, and blog/compare JSON-LD can reuse the same headline/description via BlogSchema overrides. Legal pages share legalMetadata with full OG/Twitter and English-only alternates without marketing padding.

Pricing and /docs/agent-integrations/oh-my-pi are treated as en/ja-only fallback content: middleware negotiates locale, redirects unsupported prefixed URLs, sets hreflang Link headers, sitemap/agent indexes list only those locales, and docs search skips untranslated locales. ContentLocaleLink points nav/footer/docs links at canonical English or /ja/... hrefs as appropriate.

Reviewed by Cursor Bugbot for commit 300afa6. Bugbot is set up for automated code reviews on this repo. Configure here.


Summary by cubic

Fixes localized SEO metadata from the Ahrefs audit and locks down en/ja-only fallback-content routing for Pricing and Oh My Pi. Centralizes route-specific SEO copy so pages select complete, localized titles/descriptions with consistent canonical/OG/Twitter/JSON‑LD.

  • Bug Fixes

    • Titles/descriptions: enforce 30–60/110–160 search-width bounds with Unicode- and grapheme‑safe counts (incl. Khmer); preserve over‑bound authored copy; keep source language when best; join sentences/Q&A with localized punctuation; avoid ellipses; reject HTML/placeholders/lead‑ins; and skip fragments with trailing colons or duplicate punctuation. Home allows short CJK titles and aligns OG/Twitter with the page description.
    • Route identity: new helpers in i18n/audited-seo.ts plus seoTitle/seoDescription pick contextual authored fallbacks; blog/compare JSON‑LD can reuse the exact <meta> headline/description via BlogSchema overrides; unique compare‑page titles are enforced.
    • Canonicals/social: set openGraph.url from the canonical and emit full Open Graph/Twitter cards in the content locale. Legal pages share legalMetadata for English‑only canonicals without marketing padding.
    • Audited pages: home, assets, community, best‑terminal, blog index/history, compare index/pages, pricing, and Oh My Pi now read bounded, localized copy via i18n/audited-seo.ts.
  • Fallback Content & Links

    • Middleware for en/ja‑only routes (Pricing, Oh My Pi): negotiates locale from NEXT_LOCALE and Accept-Language (q‑values, wildcards), rewrites bare paths to canonical English, 307s to /ja/... when preferred, 301s unsupported locale prefixes, and sets Link hreflang for en/ja.
    • Linking/indexing: sitemap and agent‑readable pages list only those locales; docs search indexes only locales with content. ContentLocaleLink emits direct English hrefs (no /en) or /ja/... when translated and is used in the header, nav, footer, docs sidebar/pager, and the Pro banner.

Written for commit 300afa6. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • SEO Improvements
    • Updated page metadata titles/descriptions across landing, blog, docs, community, compare, pricing, and the locale layout to use shared, locale-aware SEO helpers.
    • Improved snippet length handling (configurable min/max), Unicode-safe truncation, and consistent Open Graph/Twitter title/description output.
    • Applied minLength: 110 for descriptions where applicable.
  • Legal Pages
    • Standardized EULA, privacy policy, and Terms of Service metadata via a shared legal-page helper.
  • Localization & Fallback Content
    • Added fallback-content support for selected routes, including sitemap localization and middleware rewrite/redirect behavior.
  • Tests
    • Expanded SEO helper coverage for localized formatting, length bounds, ellipsis behavior, and grapheme cluster safety.

@vercel

vercel Bot commented Jul 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Jul 11, 2026 5:48pm
cmux-staging Building Building Preview, Comment Jul 11, 2026 5:48pm

@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

SEO helpers now normalize localized titles and descriptions with length-aware, grapheme-safe processing. Route metadata uses shared SEO, Open Graph, Twitter, canonical URL, legal-page, and fallback-content utilities. Tests cover localized output, truncation, routing, and sitemap behavior.

Changes

SEO metadata standardization

Layer / File(s) Summary
SEO helper rules and validation
web/i18n/seo.ts, web/tests/seo.test.ts
Localized suffixes, configurable metadata bounds, grapheme-safe truncation, title enrichment, and related tests were added or updated.
Fallback-content locale routing
web/i18n/locale-availability.ts, web/proxy.ts, web/app/sitemap.ts, web/tests/seo.test.ts
Fallback locales and paths now control request rewrites, redirects, localized sitemap entries, and routing tests.
Localized route metadata adoption
web/app/[locale]/(landing)/*, web/app/[locale]/layout.tsx, web/app/[locale]/pricing/page.tsx
Localized routes now derive titles and descriptions through shared helpers; documentation and pricing metadata include fallback-aware alternates, Open Graph, and Twitter fields.
Shared legal-page metadata
web/app/[locale]/(legal)/legal-metadata.ts, web/app/[locale]/(legal)/*/page.tsx
Legal pages use shared metadata generation for descriptions, canonical URLs, Open Graph, and Twitter fields.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Browser
  participant proxy
  participant localeAvailability
  participant RouteMetadata
  participant SEOHelpers
  Browser->>proxy: request localized fallback-content route
  proxy->>localeAvailability: resolve fallback content
  localeAvailability-->>proxy: path and locale availability
  proxy-->>Browser: rewrite or redirect
  Browser->>RouteMetadata: request page metadata
  RouteMetadata->>SEOHelpers: build title and description
  SEOHelpers-->>RouteMetadata: normalized SEO values
  RouteMetadata-->>Browser: metadata with alternates, Open Graph, and Twitter fields
Loading

Possibly related PRs


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 2 warnings)

Check name Status Explanation Resolution
Cmux Full Internationalization ❌ Error legalMetadata adds OG/Twitter copy for all locales using hardcoded English and locale='en', despite translated route labels in web/messages/*.json. Make legalMetadata locale-aware: source title/summary from next-intl (or per-locale strings) and add matching entries for every supported locale, or limit it to translated locales only.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ⚠️ Warning The summary and testing sections are present, but Demo Video, Review Trigger, and Checklist sections from the template are missing. Add the missing template sections, or mark Demo Video as N/A, include the review trigger block, and complete the checklist items.
✅ Passed checks (22 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Cmux Swift Actor Isolation ✅ Passed PASS: The PR diff contains no Swift files, so it cannot introduce or worsen Swift actor-isolation issues.
Cmux Swift Blocking Runtime ✅ Passed PASS: The PR diff touches only web TypeScript files; no Swift files or blocking primitives appear in the changed range.
Cmux Browser Automation Off-Main ✅ Passed PR only changes web SEO/metadata, sitemap, and proxy files; it does not touch the Swift browser socket automation paths or policy tests scoped by the rule.
Cmux Expensive Synchronous Load ✅ Passed The PR changes only web TypeScript files; no Swift files or synchronous agent-history loads are introduced or worsened, so the Swift rule is not applicable.
Cmux Cache Substitution Correctness ✅ Passed Only change is a middleware rewrite/redirect for fallback SEO routes; no cached-vs-fresh read swap or persistence/history/snapshot path is involved.
Cmux No Hacky Sleeps ✅ Passed Changed files only adjust SEO metadata and tests; no new sleeps, timers, polling, or wall-clock waits were introduced in production code.
Cmux Algorithmic Complexity ✅ Passed The diff only adds bounded metadata helpers and locale checks over tiny fixed-size arrays/strings, with no new scalable rescans or hot-path collection work.
Cmux Swift Concurrency ✅ Passed Diff only changes web/TypeScript files (web/proxy.ts, web/tests/seo.test.ts); no Swift code or concurrency patterns were introduced.
Cmux Swift @Concurrent ✅ Passed The PR diff changes only web files; no Swift files were modified, so the Swift concurrency annotation rule does not apply.
Cmux Swift File And Package Boundaries ✅ Passed PASS: The diff only touches web/proxy.ts and web/tests/seo.test.ts; no Swift files or package-boundary concerns are present.
Cmux Swiftpm Lockfiles ✅ Passed PR changes only touch web/proxy.ts and web/tests/seo.test.ts; no SwiftPM, Xcode, .gitignore, or workflow dependency files changed.
Cmux Swift Logging ✅ Passed No Swift files were changed in the PR diff, so the Swift logging rules are not applicable.
Cmux User-Facing Error Privacy ✅ Passed Production changes only touch SEO metadata/routing; no user-facing errors, alerts, or API error text were added, and no sensitive internal details are exposed.
Cmux Swiftui State Layout ✅ Passed The PR only changes web TS/TSX files; no Swift/SwiftUI files or state/layout patterns appear in the diff, so the rule is not applicable.
Cmux Architecture Rethink ✅ Passed PR only changes web TypeScript files; no Swift code or architecture patterns are present, so the Swift rethink rule is not applicable.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR diff vs main touches only web files; there are no .swift changes, so the Swift auxiliary-window shortcut rule is not applicable.
Cmux Source Artifacts ✅ Passed All changed paths are intentional source/test code files; no logs, caches, screenshots, temp dirs, or other artifact paths appear in the diff.
Cmux No Test Or Debug Seam In Production Source ✅ Passed PR only changes web TS files; no Swift production Sources/ files or test-debug seams are present.
Cmux No Ambient Global State ✅ Passed PR only changes TypeScript/TSX files (web/proxy.ts, web/tests/seo.test.ts); no Swift files or top-level Swift state changes are present, so the Swift ambient-state rule is inapplicable.
Title check ✅ Passed The title is concise and accurately summarizes the main change: localized SEO metadata updates from an Ahrefs audit.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat-seo-site-audit

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread web/app/[locale]/(landing)/blog/page.tsx Outdated
@greptile-apps

greptile-apps Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR improves localized SEO metadata and fallback-content routing across public web pages. The main changes are:

  • Centralized locale-aware title and description selection.
  • Aligned metadata with Open Graph, Twitter, and JSON-LD output.
  • Added English and Japanese fallback routing for pricing and oh-my-pi.
  • Updated sitemap, navigation, and agent-readable page locale handling.
  • Expanded metadata and Unicode coverage in SEO tests.

Confidence Score: 5/5

This looks safe to merge.

  • The updated helpers preserve complete strings rather than slicing at UTF-16 boundaries.
  • Joined emoji, combining sequences, and Khmer metadata are covered by the updated tests.
  • No blocking issue related to the earlier Unicode failure remains in the changed code.

Important Files Changed

Filename Overview
web/i18n/seo.ts Selects complete metadata candidates without splitting Unicode characters.
web/i18n/audited-seo.ts Centralizes route-specific localized SEO copy and candidate selection.
web/i18n/locale-availability.ts Defines English and Japanese availability for fallback-content routes.
web/proxy.ts Adds locale negotiation and canonical redirects for fallback-content routes.
web/tests/seo.test.ts Expands coverage for localized metadata bounds and complete Unicode sequences.

Reviews (25): Last reviewed commit: "Avoid invalid localized SEO fallbacks" | Re-trigger Greptile

Comment thread web/i18n/seo.ts Outdated
function truncateMetadataText(value: string, maxLength: number) {
if (value.length <= maxLength) return value;

const candidate = value.slice(0, maxLength - 1).trimEnd();

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.

P2 Truncation Splits Unicode Characters

slice() limits UTF-16 code units rather than complete Unicode characters. When translated metadata places an emoji or another supplementary character across the cutoff, this creates an unpaired surrogate, so the rendered title or description can contain a replacement character before the ellipsis. Truncate on grapheme boundaries before applying the punctuation and word-boundary logic.

Rule Used: Flag production user-facing text that is not fully... (source)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 710120a. Truncation now uses Intl.Segmenter grapheme clusters, so supplementary characters, combining sequences, and joined emoji stay intact. The regression test cuts across a joined emoji sequence.

— Claude Code

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@web/app/`[locale]/(landing)/docs/agent-integrations/oh-my-pi/page.tsx:
- Line 17: Update the oh-my-pi page title initialization to use the localized
seoTitle(locale, t("metaTitle")) helper instead of the raw t("metaTitle") value,
matching the title handling used by the other pages.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5d555339-e5bb-4f4d-a5ef-25daf87f52cd

📥 Commits

Reviewing files that changed from the base of the PR and between 272aee3 and d39be53.

📒 Files selected for processing (15)
  • web/app/[locale]/(landing)/assets/page.tsx
  • web/app/[locale]/(landing)/blog/cmux-history/page.tsx
  • web/app/[locale]/(landing)/blog/page.tsx
  • web/app/[locale]/(landing)/community/page.tsx
  • web/app/[locale]/(landing)/compare/[slug]/page.tsx
  • web/app/[locale]/(landing)/compare/page.tsx
  • web/app/[locale]/(landing)/docs/agent-integrations/oh-my-pi/page.tsx
  • web/app/[locale]/(legal)/eula/page.tsx
  • web/app/[locale]/(legal)/legal-metadata.ts
  • web/app/[locale]/(legal)/privacy-policy/page.tsx
  • web/app/[locale]/(legal)/terms-of-service/page.tsx
  • web/app/[locale]/layout.tsx
  • web/app/[locale]/pricing/page.tsx
  • web/i18n/seo.ts
  • web/tests/seo.test.ts

Comment thread web/app/[locale]/(landing)/docs/agent-integrations/oh-my-pi/page.tsx Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@web/tests/seo.test.ts`:
- Around line 54-61: Update the “keeps truncated descriptions above the minimum
boundary” test to measure description length with the same Intl.Segmenter
grapheme-counting approach used by the later assertions, replacing both
Array.from(description).length checks while preserving the 110–160 bounds.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: b96c7a9e-c127-471b-829e-57dbdb129ab6

📥 Commits

Reviewing files that changed from the base of the PR and between d39be53 and 710120a.

📒 Files selected for processing (5)
  • web/app/[locale]/(landing)/blog/cmux-history/page.tsx
  • web/app/[locale]/(landing)/blog/page.tsx
  • web/app/[locale]/(landing)/docs/agent-integrations/oh-my-pi/page.tsx
  • web/i18n/seo.ts
  • web/tests/seo.test.ts

Comment thread web/tests/seo.test.ts Outdated
Comment thread web/app/[locale]/layout.tsx Outdated
url: alternates.canonical,
},
twitter: twitterSummary("en", title, description),
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Legal metadata skips bounds

Low Severity

The new legalMetadata helper sets raw title strings and calls seoDescription without { minLength: 110 } or seoTitle, unlike the other pages updated in this PR. Legal titles stay well under the 30-character floor and descriptions typically remain under 110 characters after only the default 90-character padding, so Ahrefs-style bounds are not applied on EULA, Privacy, and Terms.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit abcbf95. Configure here.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@web/tests/seo.test.ts`:
- Around line 222-232: Export the production metadataSearchLength helper from
web/i18n/seo.ts, then update web/tests/seo.test.ts to import and use it instead
of the local searchWidthSegmenter, wideSearchGrapheme, and searchSnippetLength
implementations. Remove the duplicated test-local regex and weighting logic so
tests exercise the production behavior directly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: ebdc1cbc-b5d6-4c1c-92a8-39ea26074d35

📥 Commits

Reviewing files that changed from the base of the PR and between ea18e20 and abcbf95.

📒 Files selected for processing (7)
  • web/app/[locale]/(landing)/docs/agent-integrations/oh-my-pi/page.tsx
  • web/app/[locale]/pricing/page.tsx
  • web/app/sitemap.ts
  • web/i18n/locale-availability.ts
  • web/i18n/seo.ts
  • web/proxy.ts
  • web/tests/seo.test.ts

Comment thread web/tests/seo.test.ts Outdated
Comment thread web/app/[locale]/(legal)/legal-metadata.ts Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

There are 3 total unresolved issues (including 2 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 7bbfe14. Configure here.

Comment thread web/i18n/seo.ts Outdated
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.

1 participant