GEO (AI answer engines)
Claude skill atlas-geo · for Editor, Frontend
Generative Engine Optimization for the Atlas: being found, understood and cited by ChatGPT, Claude, Perplexity, Copilot and Google AI Overviews. Covers the robots.txt policy for AI bots, llms.txt and llms-full.txt, .md twins, server-rendered text for non-JS crawlers, answer-first writing, quotable sourced facts, entity clarity (Wikidata), honest freshness and geographic signals. Use when writing or templating content, adding a public page type, or changing robots, llms.txt or markdown routes.
GEO: being cited by AI answer engines
Answer engines quote pages they can fetch without JavaScript, understand as clear entities,
trust (sources, named authors, honest dates) and lift as self-contained passages. Classic SEO
(atlas-seo) is the base: Google's AI features need no special files, only an indexable page
with good text. Everything here adds to it.
Before you start
- Related skills:
atlas-seo(canonical, sitemap, dates, JSON-LD builders),atlas-content(house style for writers),atlas-architecture(static/ISR rule),atlas-maps-data(globe, indicators),atlas-performance. - Decision record: ADR-021 in
docs/architecture/10-rozhodnuti-adr.md(open to AI crawlers). - Tests:
src/lib/seo/llms-robots.test.ts,src/lib/seo/markdown.test.ts,src/features/seo/documents.test.ts, e2etests/seo.spec.ts. - Bot tables, llms.txt anatomy and evidence: reference.
Rules
Crawler access (robots.txt)
- Today's policy (owner decision): every search engine, AI search/assistant bot and AI training
bot is allowed.
ALLOWED_BOTSinsrc/lib/seo/robots.tsnames them in three groups, androbotsRules()gives the named group the same rules as*: allow/, disallow only/api/,/admin,/auth/andPRIVATE_PATHS. - Never block a search or retrieval token: Googlebot, Bingbot, OAI-SearchBot, ChatGPT-User, Claude-SearchBot, Claude-User, PerplexityBot, Perplexity-User. Blocking OAI-SearchBot alone removes the Atlas from ChatGPT search answers.
- Training tokens (GPTBot, ClaudeBot, CCBot, Google-Extended, Applebot-Extended, Amazonbot,
Meta-ExternalAgent) stay allowed. Closing one is an owner decision; do it by moving the
token into its own
Disallowgroup, never by IP or user-agent blocking. Disallowing Google-Extended also stops Gemini grounding, but not Google Search. - Bots must reach robots.txt and pages: make sure Vercel Firewall or bot protection never challenges or 403s the allowed bots (Vercel team → project → Firewall).
- A new bot token goes into the right group of
ALLOWED_BOTSwith its operator, andllms-robots.test.tsis updated.
Rendering for crawlers that don't run JavaScript
- GPTBot, OAI-SearchBot, ClaudeBot, PerplexityBot and CCBot fetch HTML and do not execute JS. Main text, headings, numbers with their sources, author, dates and navigation links must be in the server-rendered HTML.
- Every client-only view (globe, charts, embeds in
VisualsCarousel, map visuals) has a text equivalent (rule inatlas-maps-data). For crawlers it must be in the server HTML: a short summary and the key numbers with source and year. - Public routes stay prerendered (
atlas-architecture; CI guardscripts/check-static-routes.mjs). Next 16 caveat: on a dynamic route,generateMetadataoutput is streamed into<body>for any user agent not inhtmlLimitedBots. The default list (node_modules/next/dist/shared/lib/router/utils/html-bots.js) covers Google, Bing and social bots but not GPTBot, OAI-SearchBot, ClaudeBot, Claude-SearchBot or PerplexityBot. Those bots would then miss title, canonical and description. Keep the route static. If one ever must be dynamic, sethtmlLimitedBotsinnext.config.tsto the default pattern plus those tokens (the option replaces the default list, it doesn't extend it). - Keep redirects to one hop and old slugs as 308 forever (
atlas-seo): AI indexes keep stale URLs for a long time.
llms.txt and llms-full.txt
/llms.txtand/llms-full.txtare route handlers (src/app/[locale]/llms.txt/route.ts,llms-full.txt/route.ts, bothllmsRoute()fromsrc/features/seo/feeds.ts). Content comes fromgetLlmsTxt/getLlmsFullTxt(src/features/seo/queries.ts); the format is built byllmsTxt/llmsFullTxt(src/lib/seo/llms.ts).- They read the same cached DB queries as the sitemap, so they never list a page that 404s. Never hand-edit a static llms file.
- Shape (llmstxt.org): H1 site name,
>summary, short detail paragraphs, H2 sections of- [Name](url): note, a final## Optionalsection. Keep the citation-format note. - A new public section is added to
getLlmsTxtin the same PR that adds it to the sitemap. llms-full.txtis about 800 KB. Split it per section if it grows past a few MB.- Every page links it: the
[locale]layout emits<link rel="describedby">to llms.txt and the RSS/Atom alternates. Keep them. - llms.txt is a community proposal, not a standard, and Google ignores it. Keep it accurate; don't invest beyond that.
Markdown twins
- Articles have
.mdtwins:/topics/<slug>.mdand/news/<slug>.md.src/proxy.tsrewrites them tosrc/app/[locale]/md/[kind]/[slug]/route.ts, which rendersarticleMarkdown()(src/features/seo/documents.ts) with an HTTPLink: <html url>; rel="canonical"header. - A page advertises its twin with
pageMetadata({ markdown: true })(<link rel="alternate" type="text/markdown">). Set it only when the twin exists: never advertise a 404. - Adding a twin for another page type means extending the route's
kindcheck andgenerateStaticParams, the proxy's.mdrewrite, a document builder indocuments.ts, andmarkdown: trueon that page, in one PR.
Answer-first content (editors and templates)
- Open every topic, news item, country, region and issue page with an "In short" answer of 2–3 sentences that names the entity, the place and the time ("As of 2025, …").
- For topics, editors write it in the article's SEO form, field "In short (GEO)"
(
geo_summary, maxGEO_SUMMARY_MAX= 800 characters).EncyclopediaArticleshows it at the top of the dossier and the page feeds it to JSON-LDabstract. Any new "In short" field works the same way: visible first, then structured data. - Write sentences that stand alone when quoted: never start with "This", "It" or "The above".
- Use question-shaped H2/H3 headings where natural ("Who counts as a refugee?"), each followed by a direct one- or two-sentence answer.
- Every statistic in text has value, unit, year, named source and a link, in HTML text, not
only in a chart or on the globe. Indicator cards already do this (
MetricCards,SourceLinkinsrc/components/atlas/ui.tsx). - Quote primary voices (treaty text, a UN body, a named expert) briefly, in
<blockquote>with<cite>. Cite sources inline as real<a href>links to primary sources (UNHCR, World Bank, UNDP, V-Dem) and keep the resource list (it becomes JSON-LDcitation). - Never stuff keywords or lists of place names: it scores below baseline in the GEO study and is a Google spam pattern.
- Don't hide key answers in images, PDFs or closed widgets. Tabs and accordions are fine.
Entity clarity
- One canonical name per entity everywhere: title, H1, JSON-LD
name, llms.txt. Formal or other names go intoalternateName(countries:nameFormal). - Link entities to Wikidata. Countries:
countrySameAs(iso3)readssrc/config/wikidata-countries.json(Wikidata + English Wikipedia). The organization'ssameAscomes fromsrc/config/organization.tsand site settings. AddsameAsfor a region or issue only when a matching Wikidata item really exists. - Articles name their places with the same
@ids:about(countries) andcontentLocation(region) inarticleNode. - Named authors with a real profile page are the strongest "who" signal; author rules are in
atlas-seo.
Freshness, honestly
- Visible "Published"/"Updated" dates, JSON-LD dates and sitemap
lastmodagree and move only on real content changes. That rule lives inatlas-seo(§ Dates, § Sitemaps). Never refresh dates to look current: engines learn to ignore them. - Say "As of <year>" in text next to time-sensitive facts, so a quoted sentence stays true.
- IndexNow (
atlas-seo) is the fast path into Bing, which grounds Copilot and ChatGPT search.
Geographic signals
- Place identity lives in JSON-LD, not meta tags.
countryNodeemitsCountrywith ISO 3166-1 alpha-2/alpha-3identifiers, Wikidata/WikipediasameAs,geo(GeoCoordinates),containedInPlace(region) and indicator values with source and year (additionalProperty).groupPlaceNodeemits a region or issue withcontainsPlace(its countries). geoMeta()(src/lib/seo/index.ts) writesgeo.position,geo.region,ICBMandplace:location:*. Google ignores them; they are legacy and harmless. Use them only for real places (countries, regions). Don't add new ones and don't spend effort on them.
Measuring
- Google AI Overviews and AI Mode clicks are inside Search Console Performance → "Web"; there is no separate filter. Bing Webmaster Tools has an AI Performance report (Copilot citations). Links: atlas-seo reference.
- Watch AI referrers (
chatgpt.com,perplexity.ai,claude.ai,copilot.microsoft.com) in Vercel Analytics, and bot user agents in the Vercel logs.
Checklist
- [ ] No search or retrieval bot blocked; robots change has an owner decision behind it.
- [ ] Main text, numbers with sources, author and dates are in the server HTML (view source).
- [ ] Route stays prerendered (no
htmlLimitedBotsworkaround needed). - [ ] New public section is in
getLlmsTxtand the sitemap. - [ ]
markdown: trueonly where the.mdtwin returns 200. - [ ] Page opens with a visible "In short" answer naming entity, place and time.
- [ ] Every statistic: value, unit, year, source, link, as text.
- [ ] Entity names consistent;
sameAsonly to the right Wikidata item. - [ ] No keyword or place-name stuffing; no fake freshness.
Anti-patterns
- Blocking AI bots "to protect content": the Atlas exists to be read and cited (ADR-021).
- Putting the key numbers only on the globe or in a chart: non-JS crawlers see nothing.
- A JSON-LD
abstractor FAQ that readers can't see: a mismatch, and untrusted. - Making a public route dynamic: metadata streams into
<body>for AI bots, and the CDN cache is lost (atlas-performance). - Writing for bots (place-name lists, "best atlas" phrases): measurably worse.
Known gaps (2026-10-16)
- Map pages carry little server-rendered text: about 1.0 k characters on
/, 1.5 k on/region/east-asia, 2.0 k on/global-issue/energy-transition, 2.4 k on/country/china, against about 28 k on a topic. Add a server-rendered "In short", a key-facts block with source and year, and links to related articles. - No
.mdtwins for countries, regions and issues (/country/china.mdreturns 404). - Only topics have an editor-written "In short" (
geo_summary, rendered byEncyclopediaArticle). News, countries, regions and issues have no equivalent field yet. geoMetais also emitted on global issues and on news (with region centroids such as0;20); an issue is not a place. Low priority.
Sources
GEO paper, Aggarwal et al., KDD 2024 · Google, AI features and your website · Microsoft, content for AI search answers · llms.txt · Vercel, AI crawlers and JS. Full list in reference.
Files of this skill
reference.md
GEO reference
Detail for atlas-geo.
Bots by class
The Atlas allows all of these today (ALLOWED_BOTS, src/lib/seo/robots.ts).
Search and retrieval (must stay allowed)
| Token | Operator | What it does | Notes |
|---|---|---|---|
| Googlebot | Search, AI Overviews, AI Mode | robots.txt also governs Google's AI features; renders JS | |
| Bingbot | Microsoft | Bing, grounding for Copilot and ChatGPT search | in Next's htmlLimitedBots default |
| OAI-SearchBot | OpenAI | ChatGPT search results | a robots change takes about 24 h; IPs published by OpenAI |
| ChatGPT-User | OpenAI | fetches a page a user asked about | robots.txt may not apply |
| Claude-SearchBot | Anthropic | Claude's search index | blocking reduces visibility in Claude answers |
| Claude-User | Anthropic | fetches a page a user asked about | |
| PerplexityBot | Perplexity | Perplexity search index | not used for training |
| Perplexity-User | Perplexity | fetches a page a user asked about | generally ignores robots.txt |
| Applebot, DuckDuckBot, SeznamBot, YandexBot, DuckAssistBot, MistralAI-User, Amzn-SearchBot | others | search and assistants | listed in ALLOWED_BOTS |
Training (owner policy: allowed)
| Token | Effect of a Disallow |
|---|---|
| GPTBot | out of OpenAI model training; no effect on ChatGPT search |
| ClaudeBot | out of Anthropic training; honours Crawl-delay |
| Google-Extended | out of Gemini training and grounding; no effect on Google Search (a control token, no own crawler) |
| Applebot-Extended | out of Apple model training (control token) |
| CCBot | out of Common Crawl, a widely used training corpus |
| Amazonbot, Meta-ExternalAgent | out of Amazon / Meta AI use |
Why allowed: the Atlas is an open-access non-profit; models that have read it are more likely to know and name it (ADR-021).
Verify a bot by its operator's published IP list, not by the user-agent string alone.
What non-JS crawlers see
| Crawler | Runs JS |
|---|---|
| Googlebot (and Gemini via Google's index), Applebot | yes |
| GPTBot, OAI-SearchBot, ChatGPT-User, ClaudeBot, PerplexityBot, CCBot | no |
Quick check of what a non-JS bot gets:
curl -s -A "OAI-SearchBot" https://atlas-of-todays-world.vercel.app/country/china \
| sed -e 's/<script[^>]*>.*<\/script>//g' -e 's/<[^>]*>/ /g' | tr -s ' \n' | wc -c
curl -s -A "GPTBot" https://atlas-of-todays-world.vercel.app/country/china | grep -c '<link rel="canonical"'The second line must print 1: metadata is in <head> (a prerendered page). On a dynamic route
without htmlLimitedBots it would be streamed into <body> for this user agent.
Next 16 default htmlLimitedBots pattern (from node_modules/next/dist/shared/lib/router/utils/html-bots.js
in 16.4.0) includes -Google/Google- crawlers, Chrome-Lighthouse, Slurp, DuckDuckBot,
baiduspider, yandex, Bingbot, applebot, facebookexternalhit, Twitterbot, LinkedInBot, Slackbot,
Discordbot, WhatsApp and a few more. Re-read the file after a Next upgrade.
llms.txt anatomy (as generated)
# Atlas of Today's World
> One-sentence summary of the organization and the site.
Publisher, licence of use, how data is sourced, "Cite as" format, language, links to
llms-full.txt, feeds and sitemap.
## Encyclopedia dossiers
- [Title](https://…/topics/slug): YYYY-MM-DD — first sentences…
## News and analysis · ## World regions · ## Global issues
## Data layers (datasets by country) · ## About the Atlas
## Optional
- [Name](url): noteLive: llms.txt (about 44 KB), llms-full.txt (about 800 KB), example twin types-of-migrants.md.
Evidence behind the writing rules
GEO study (Aggarwal et al., KDD 2024) on its GEO-bench, measured by position-adjusted word count in generative answers:
- adding quotations from relevant sources: about +40 %
- adding statistics: about +30 %
- citing sources: about +28 %
- fluency rewrites helped; keyword stuffing scored below baseline
- gains were largest for lower-ranked sources (rank-5 sources gained about +100 %)
- by domain: citing sources helped most for law and government and for facts; quotations for history and people and society; statistics for law and government and for debate. Results were confirmed on Perplexity.
Google (May 2025): unique, non-commodity content, important content in text, structured data that matches visible text, good images, good page experience. No special AI files or markup.
Microsoft (October 2025): assistants split pages into passages. Clear H2/H3 sections, direct one-to-two-sentence answers, self-contained sentences, lists and tables for comparisons, measurable facts; nothing key hidden in tabs, PDFs or images.
"In short" pattern
Good (template): "As of <year>, <country> <verb> <number + unit> <what>, according to <named source, linked>. <One sentence of context that is true on its own.>"
Weak: "This page explains the situation in the country and its many challenges."
Shape: entity + time + fact with number + named source; second sentence gives context; no pronoun openers; no claims the page body doesn't support.
Measuring
- Search Console Performance: AI Overviews and AI Mode are counted under "Web".
- Bing Webmaster Tools: AI Performance report (public preview since February 2026) shows Copilot and Bing AI citations per page.
- Vercel Analytics (cookie-less,
@vercel/analyticsinsrc/app/layout.tsx): referrerschatgpt.com,perplexity.ai,claude.ai,copilot.microsoft.com. Open it from the Vercel team → project → Analytics. - Vercel logs (project → Logs): filter by user agent (OAI-SearchBot, ChatGPT-User, Claude-User, PerplexityBot, Perplexity-User) to see retrieval traffic.
Sources
- GEO paper: https://arxiv.org/abs/2311.09735 (full text https://arxiv.org/html/2311.09735v3)
- Google, AI features and your website: https://developers.google.com/search/docs/appearance/ai-features
- Google, succeeding in AI search (May 2025): https://developers.google.com/search/blog/2025/05/succeeding-in-ai-search
- Microsoft, optimizing content for AI search answers: https://about.ads.microsoft.com/en/blog/post/october-2025/optimizing-your-content-for-inclusion-in-ai-search-answers
- Google common crawlers and Google-Extended: https://developers.google.com/search/docs/crawling-indexing/google-common-crawlers
- OpenAI bots: https://developers.openai.com/api/docs/bots
- Anthropic crawlers: https://support.claude.com/en/articles/8896518-does-anthropic-crawl-data-from-the-web-and-how-can-site-owners-block-the-crawler
- Perplexity bots: https://docs.perplexity.ai/guides/bots
- Common Crawl CCBot: https://commoncrawl.org/ccbot
- Vercel, the rise of the AI crawler: https://vercel.com/blog/the-rise-of-the-ai-crawler
- llms.txt proposal: https://llmstxt.org/
- Bing, sitemaps in AI-powered search: https://blogs.bing.com/webmaster/July-2025/Keeping-Content-Discoverable-with-Sitemaps-in-AI-Powered-Search
- Bing AI Performance report (secondary): https://searchengineland.com/bing-webmaster-tools-testing-new-ai-performance-report-468039
- schema.org Place and Country: https://schema.org/Place, https://schema.org/Country, https://schema.org/about
- Google, supported meta tags (geo tags ignored): https://developers.google.com/search/docs/crawling-indexing/special-tags
- Next.js 16.4 docs:
node_modules/next/dist/docs/01-app/03-api-reference/04-functions/generate-metadata.md(Streaming metadata),…/05-config/01-next-config-js/htmlLimitedBots.md