> ## Documentation Index
> Fetch the complete documentation index at: https://docs.yournarrative.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# The audit library

> Every technical and content check Narrative runs against your pages, grouped by family, with severity, scope and which engines it affects

Every page NarrativeBot crawls is judged against this library. A check is
either **per page**, reading that page's own response, or **whole site**,
reading the link graph and the site files, because a page cannot know what
links to it, how deep it sits, or that another page is a copy of it.

Each check produces a finding that says what it found, what it expected, and
its severity. Findings on the same family and page roll up into one piece of
work rather than a dozen tiny ones, and a family is the unit a
[Repair](https://docs.yournarrative.ai/technical-seo/repairs-and-fixes.md) is opened on.

> [!NOTE]
>
> **Status** says whether the check runs today. A check marked **Coming** is in
> the catalogue and records nothing yet, rather than a fake pass. The library
> grows by release; this page is kept in step with the code.

## Reading the table

| Column | Meaning |
| - | - |
| **Check** | The display name and the key you will see in the API and in exports. |
| **What passes** | The rule the page or site is held to. |
| **Severity** | Gate, High, Medium or Low. See [severity](https://docs.yournarrative.ai/technical-seo/overview.md#severity). Gates are the two faults that make a brand invisible to an engine outright. |
| **Scope** | Per page, or whole site (needs the full crawl and the link graph). |
| **Engines** | Who the check matters for. *Answer engines* means the non-Google crawlers (OAI-SearchBot, PerplexityBot, Claude-User and their peers), which do not run JavaScript. |

## Technical checks

The technical half of the library covers whether an engine can fetch, render,
read and trust the page. Two of these are gates.

### Crawler access

| Check | What passes | Severity | Scope | Engines | Status |
| - | - | - | - | - | - |
| **AI crawler access in robots.txt** `ai_bot_access` | robots.txt must allow OAI-SearchBot, PerplexityBot, ClaudeBot, and Google-Extended. Blocking any of these makes the brand invisible to that engine. | Gate | Whole site | Search and answer engines | Live |
| **CSS/JS resources unblocked for Googlebot** `resources_unblocked` | robots.txt does not block CSS or JS files Googlebot needs to render the page correctly. | Medium | Whole site | Google | Live |

### Renderability

| Check | What passes | Severity | Scope | Engines | Status |
| - | - | - | - | - | - |
| **SSR without JavaScript** `ssr_no_js` | Critical page content must be present in the raw HTML response with JavaScript disabled. Non-Google engines (ChatGPT, Perplexity, Claude) do not execute JS. | Gate | Per page | Answer engines | Live |
| **Answer is extractable from plain DOM text** `extractable` | Answer content lives in plain DOM text: not an image, an accordion-hidden section, or behind a login wall. Extractors cannot read what they cannot see. | High | Per page | Search and answer engines | Live |

### Indexability

| Check | What passes | Severity | Scope | Engines | Status |
| - | - | - | - | - | - |
| **Indexable with snippet allowed** `indexable_snippet` | Page is indexed and does not carry noindex, nosnippet, or max-snippet:0. Without snippet permission AI Overviews cannot quote the page. | High | Per page | Google | Live |
| **XML sitemap submitted to GSC** `xml_sitemap` | An XML sitemap listing canonical URLs is submitted and healthy in Google Search Console. | Medium | Whole site | Google | Live |
| **Correct HTTP status codes** `http_status` | Pages return correct status codes: 200 for indexable, 301/302 for redirects, 401 for auth, 404 for missing. Only 200 pages are indexed. | Medium | Per page | Google | Live |
| **SPA not-found emits real 404 or noindex** `no_soft_404_spa` | Single-page-app 'not found' states emit a real HTTP 404 or a noindex meta tag, not a 200 with an error message (soft-404). | Medium | Per page | Google | Live |
| **Google Search Console verified** `gsc_verified` | GSC property is verified via HTML tag, DNS, GA, or GTM. Prefer DNS domain-property for full-site coverage. Token is persistent and reachable without auth. | Medium | Per page | Google | Coming |

### Canonical URLs

| Check | What passes | Severity | Scope | Engines | Status |
| - | - | - | - | - | - |
| **Self-referencing canonical link** `canonical` | Each indexable page carries a self-referencing rel=canonical and duplicate-content variants are minimized. | Medium | Per page | Search and answer engines | Live |
| **JS-injected canonical matches original** `canonical_js_consistent` | Any canonical URL injected by client-side JS matches the server-rendered canonical. Only one canonical per page. | Medium | Per page | Google | Coming |

### Metadata

| Check | What passes | Severity | Scope | Engines | Status |
| - | - | - | - | - | - |
| **Unique, descriptive title tag** `title_tags` | Every indexable page has a unique, descriptive `<title>`. Length in the 30-60 char range so it renders without truncation in Google's SERP. | Medium | Per page | Google | Live |
| **Unique meta description** `meta_description` | Every indexable page has a unique 1-2 sentence meta description. Not a ranking factor, but drives snippet quality when Google uses it. | Medium | Per page | Google | Live |
| **Titles are unique** `duplicate_titles` | No two pages should share a title. A repeated title splits the signal between the pages that carry it and gives a reader no way to tell them apart in a list of results. | Medium | Whole site | Search and answer engines | Live |
| **Meta descriptions are unique** `duplicate_meta_descriptions` | No two pages should share a meta description. A repeated description describes at most one of the pages using it accurately. | Medium | Whole site | Search and answer engines | Live |

### URL structure

| Check | What passes | Severity | Scope | Engines | Status |
| - | - | - | - | - | - |
| **Word-based URLs with topical directories** `descriptive_urls` | URLs use human-readable words and organize content into topical directories rather than opaque IDs. | Medium | Per page | Google | Live |
| **History-API routes, not fragment URLs** `history_api_routing` | Routes use real /path URLs via the History API, not #/fragment paths that Googlebot ignores in indexing. | Medium | Per page | Google | Live |

### Link hygiene

| Check | What passes | Severity | Scope | Engines | Status |
| - | - | - | - | - | - |
| **No orphan pages** `orphan_pages` | Every page must be reachable by at least one internal link. A page nothing links to is found only by someone who already has the URL, receives no authority from the rest of the site, and is invisible to any crawler that discovers by following links. | High | Whole site | Search and answer engines | Live |
| **Internal links resolve** `broken_internal_links` | Internal links must point to pages that load. A link into a missing page is a dead end for a reader and a wasted fetch for a crawler. Reported against the page holding the link, which is the part that can be edited. | High | Whole site | Search and answer engines | Live |
| **Crawlable `<a href>` navigation** `crawlable_links` | Site navigation and links use `<a href>` to real URLs with descriptive anchor text: not JS-only click handlers. | Medium | Per page | Google | Live |
| **No redirect chains** `redirect_chains` | An internal link should reach its destination in at most one redirect. Longer chains lose signal at every hop and are abandoned by crawlers before the end; a loop never resolves. | Medium | Whole site | Search and answer engines | Live |
| **Pages are not buried** `click_depth` | A page should sit within a few clicks of the homepage. The deeper a page is, the less often it is crawled and the less internal authority reaches it. | Medium | Whole site | Search and answer engines | Live |
| **nofollow/ugc/sponsored on untrusted links** `ugc_nofollow` | User-generated and paid outbound links carry rel=nofollow, rel=ugc, or rel=sponsored as appropriate. | Low | Per page | Google | Live |
| **Internal authority reaches every page** `internal_authority` | A page should receive a meaningful share of the site's internal link authority. A page linked only from places that carry no weight is technically reachable and practically buried. | Low | Whole site | Search and answer engines | Live |

### Entity identity

| Check | What passes | Severity | Scope | Engines | Status |
| - | - | - | - | - | - |
| **Organization schema with sameAs** `entity_sameas` | JSON-LD Organization block with sameAs links pointing to Wikidata, Wikipedia, and LinkedIn. Anchors the brand as a resolvable entity across engines. | High | Per page | Search and answer engines | Live |
| **Brand, product, category named consistently** `entity_clarity` | Brand, product, and category names are used explicitly and consistently across the page. Enables extractor attribution to the correct entity. | High | Per page | Search and answer engines | Coming |

### Image semantics

| Check | What passes | Severity | Scope | Engines | Status |
| - | - | - | - | - | - |
| **Descriptive alt text on images** `image_alt` | Images carry descriptive alt text, sit near relevant text context, and use meaningful filenames. | Medium | Per page | Google | Live |

### Freshness

| Check | What passes | Severity | Scope | Engines | Status |
| - | - | - | - | - | - |
| **Freshness signals aligned with real updates** `freshness` | Visible 'last updated' date matches the JSON-LD dateModified value and reflects a real content delta. Non-Google engines (Perplexity especially) prioritize fresh. | Medium | Per page | Answer engines | Live |

### Performance

| Check | What passes | Severity | Scope | Engines | Status |
| - | - | - | - | - | - |
| **Good Core Web Vitals / fast TTFB** `core_web_vitals` | Lighthouse Core Web Vitals in the 'Good' band and TTFB under Google's threshold. Slow pages get deprioritized in Google's AI answer selection. | Medium | Whole site | Google | Live |

### Rendering reliability

| Check | What passes | Severity | Scope | Engines | Status |
| - | - | - | - | - | - |
| **Lazy-loaded content renders without scroll** `lazy_load_safe` | Lazy-loaded content becomes visible without user scroll (intersection observer + prerender-friendly). The renderer does not scroll. | Medium | Per page | Google | Live |
| **Fingerprinted JS/CSS asset filenames** `asset_fingerprinting` | JS and CSS files use content-hash filenames so the renderer cache does not serve stale assets after a deploy. | Low | Per page | Google | Live |

### Policy compliance

| Check | What passes | Severity | Scope | Engines | Status |
| - | - | - | - | - | - |
| **Passes Google spam policies** `spam_policy` | Site passes Google's spam policies: no scraped or scaled content, no cloaking, no keyword stuffing, no auto-generated gibberish. | High | Per page | Google | Coming |
| **No intrusive interstitials** `no_interstitials` | Main content is not blocked by intrusive interstitials (popups covering the fold, forced app-install prompts). | Medium | Per page | Google | Live |

### Discovery

| Check | What passes | Severity | Scope | Engines | Status |
| - | - | - | - | - | - |
| **IndexNow protocol enabled** `indexnow` | Site publishes IndexNow keys. Bing / Copilot pick up content changes faster with IndexNow than via crawl only. | Low | Per page | Bing and Copilot | Coming |

## Content checks

The creative half judges whether a page is written to be quoted: an answer up
front, concrete sourced figures, consistent naming, and passages that stand
on their own. These use a model to read the page, so they are being switched
on family by family as each is validated. Where the audit is not yet running
a check it is marked Coming and records nothing.

### Content quality

| Check | What passes | Severity | Scope | Engines | Status |
| - | - | - | - | - | - |
| **No near-duplicate pages** `near_duplicate_content` | No page should be substantially a copy of another. Pages whose bodies differ only by a name or a city compete to answer the same question, and an engine picks one and discards the rest. | High | Whole site | Search and answer engines | Live |
| **Free of spelling and grammar errors** `spelling_grammar` | Content is free of spelling and grammar errors. Google's 'compelling and useful' guidance treats this as a baseline quality signal. | Medium | Per page | Search and answer engines | Coming |

### Structure and extractability

| Check | What passes | Severity | Scope | Engines | Status |
| - | - | - | - | - | - |
| **Direct answer in the first \~50 words** `answer_first` | Page opens with the core answer in the first \~50 words. \~44% of AI citations come from the first \~30% of the page. | High | Per page | Search and answer engines | Live |
| **Real questions as headings** `qa_format` | Real user questions used as section headings with concise answers immediately below. Maps directly to conversational AI queries. | Medium | Per page | Search and answer engines | Coming |
| **Self-contained passages** `standalone_passages` | Sections are self-contained enough to stand alone when extracted. Improves non-Google extractability. NOT a Google ranking requirement. | Medium | Per page | Answer engines | Coming |

### Evidence and citations

| Check | What passes | Severity | Scope | Engines | Status |
| - | - | - | - | - | - |
| **Concrete, sourced statistics** `quotable_stats` | Content includes concrete statistics with named sources. Princeton GEO research finds this is the largest measured lever (+41% citation rate). | High | Per page | Search and answer engines | Coming |
| **Primary sources + named-expert quotes** `cited_sources` | Content cites primary or authoritative sources and quotes named experts. Boosts citation rate \~28-40%. | High | Per page | Search and answer engines | Coming |
| **Outbound links to trusted sources** `corroborating_links` | Content links outward to trusted, authoritative sources (untrusted sources should carry nofollow). Demonstrates topical knowledge to Google. | Medium | Per page | Search and answer engines | Coming |

### Topic coverage

| Check | What passes | Severity | Scope | Engines | Status |
| - | - | - | - | - | - |
| **Comparison / 'X vs Y' / decision tables** `comparison_content` | Topic-relevant comparison content: 'best X for Y', 'X vs Y', or decision tables. Highest-citing content format for AI answer surfaces. | High | Per page | Search and answer engines | Coming |
| **Coverage of competitor-owned angles** `topic_coverage_gap` | Page/site covers topics and angles that competitors already own for the target Topic. NB: means depth on Topics the brand should own: not spammy variation-coverage which trips Google's scaled-content-abuse policy. | High | Per page | Search and answer engines | Coming |

### Originality

| Check | What passes | Severity | Scope | Engines | Status |
| - | - | - | - | - | - |
| **Original data, testing, or POV** `first_hand_value` | Page contains original data, testing, or an opinionated point of view: not a commodity restatement of what's already common. Google says this influences AI visibility more than any other factor. | High | Per page | Search and answer engines | Coming |

### Supporting media

| Check | What passes | Severity | Scope | Engines | Status |
| - | - | - | - | - | - |
| **Relevant images / video with SEO metadata** `supporting_media` | Pages include relevant images or video that follow image/video SEO best practices. AI surfaces media as additional visibility slots. | Medium | Per page | Search and answer engines | Coming |

### External signals

| Check | What passes | Severity | Scope | Engines | Status |
| - | - | - | - | - | - |
| **Google Business Profile / Merchant Center** `gbp_merchant` | Where relevant to the brand type: an active Google Business Profile and Merchant Center feed. Both feed local/product info into AI answers. | Low | Per page | Google | Coming |

## Who the crawler-access check looks for

The **AI crawler access** gate reads your robots.txt for the agents that
matter, grouped by what blocking each one costs:

| Group | Agents | Cost of blocking |
| - | - | - |
| **Retrieval** | `OAI-SearchBot`, `PerplexityBot`, `Claude-User` | These fetch a page because somebody asked a question right now. Blocking one removes you from that engine's answers immediately. |
| **Training** | `GPTBot`, `ClaudeBot`, `CCBot`, `Google-Extended` | These feed future models. Blocking costs nothing today and compounds quietly. |
| **Search** | `Googlebot`, `Bingbot` | The classic pair. Most AI answers rest on the search index underneath them. |

The finding names which group is blocked, because "invisible in ChatGPT today"
and "missing from the next model" call for different urgency.

## How findings become work

* A finding on a page is one row in **Site Health**, under the page and under
  the issue.
* Findings in the same family on a page are one opportunity, not several.
* Opening a Repair on a family creates one Fix per affected page. The next
  crawl re-judges each Fix; one the audit no longer sees closes itself.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.