Skip to content

Make indexability one predicate across hreflang, robots meta and sitemaps - #39

Merged
SaurabhPandey9752 merged 1 commit into
mainfrom
seo/market-indexability
Aug 1, 2026
Merged

Make indexability one predicate across hreflang, robots meta and sitemaps#39
SaurabhPandey9752 merged 1 commit into
mainfrom
seo/market-indexability

Conversation

@SaurabhPandey9752

Copy link
Copy Markdown
Collaborator

Prerequisite for the ARC0 runbook §F flag flips. This must deploy before NEXT_PUBLIC_REGION_ROUTING_LIVE is turned on.

Why now

Flipping R against current main wakes the India waitlist gate, which exempts only /robots.txt and /sitemap.xmlnot /sitemaps/. On .in the sitemap index would resolve, list six children, and 307 every one to /waitlist: unsubmittable in Search Console, recorded by Google as a persistent fetch failure that takes weeks of re-crawls to clear, with nothing in our logs. lib/region/crawl-paths.ts makes that exemption list single-source.

What changed

  • marketIndexable() is now the one predicate. hreflang membership, the noindex in app/layout.tsx, and sitemap contents all derive from it, so "in the cluster ⟺ indexable" holds by construction. A market is indexable only once regionRoutingLive() gives it its own content; .in additionally waits on its own launch flag. Self-heals when routing goes live — no temporary hack to remember.
  • lib/region/crawl-paths.ts — extracts the crawl-metadata exemption list that had drifted between geo-route.ts and waitlist.ts.
  • organizationSchema — keeps main's per-market ORG_ADDRESS (the .in node is the Bangalore entity) but makes areaServed follow it. main asserted the GTA city list unconditionally, so the .in node claimed a Bangalore address and a Toronto service area — a self-contradicting entity implying the India domain is really a Toronto business. Now keyed off the resolved addressCountry, so a market added to ORG_ADDRESS later can't silently inherit the wrong service area.

Measured problem this addresses

.ca / .ai / .in homepages currently differ by ~110 bytes out of ~330,000 with the hostname normalised out — the same page. All three say "Toronto"/"GTA", all three self-canonicalize, none carries noindex.

Tests

tests/seo/organization-schema.test.ts is new — organizationSchema had zero coverage before.

Gates: typecheck clean · 1450 tests pass · lint 0 errors · knip clean · migrations OK.

🤖 Generated with Claude Code

…maps

Three things had to agree about a non-home market and didn't: the hreflang
cluster, the robots meta tag, and what the sitemap lists. Only the first
existed, so turning NEXT_PUBLIC_MULTI_REGION_LIVE on would have put .in and
.ai into the cluster while they still serve byte-identical Canadian content
under a dark router — an orphaned duplicate, not a market.

marketIndexable() in lib/region/seo.ts is now the single predicate. A market
is indexable only once regionRoutingLive() gives it its own content, and .in
additionally waits on its own launch flag. hreflang membership, the noindex
in app/layout.tsx, and sitemap contents all derive from it, so the
cluster-implies-indexable invariant holds by construction and self-heals when
routing goes live rather than leaving a hack to remember.

Also extracts lib/region/crawl-paths.ts. The crawl-metadata exemption list was
written twice and had already drifted: geo-route.ts exempted /robots.txt,
/sitemap.xml and /sitemaps/, waitlist.ts only the first two. Invisible while
routing is off; at India launch .in/sitemap.xml would have resolved, listed
six children, and 307'd every one to /waitlist — unsubmittable in Search
Console with nothing in our logs. A redirected sitemap is worse than a missing
one, so this list must not live in two places.

organizationSchema keeps main's per-market ORG_ADDRESS (the .in node is the
Bangalore entity, which is better than the addressCountry-only fallback this
branch had) but makes areaServed follow it. main asserted the GTA city list
unconditionally, so the .in node claimed a Bangalore address AND a Toronto
service area — a self-contradicting entity that tells Google the India domain
is really a Toronto business. areaServed is now keyed off the resolved
addressCountry so a market added to ORG_ADDRESS later cannot silently inherit
the wrong service area. tests/seo/organization-schema.test.ts covers this;
there was no coverage of organizationSchema at all before.

Adds SEO-AUDIT.md and the review brief that calls this work partial by design.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@SaurabhPandey9752
SaurabhPandey9752 merged commit 3ed347c into main Aug 1, 2026
6 checks passed
@SaurabhPandey9752
SaurabhPandey9752 deleted the seo/market-indexability branch August 1, 2026 06:06
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