
# How we score: 23 checks, and why some weigh more than others

A score out of 100 is a claim, and most of them are unfalsifiable: a number with no method behind it, no version on it, and no way to tell later whether your site changed or the tool did.

Ours is 23 checks across five weighted dimensions, all of it in one file — `packages/engine/src/scorer.js`. Here is what each part measures, what it is worth, and what it cannot tell you.

## The five dimensions

**Accessibility — 30%, 3 checks.** Whether a machine can read the page at all. Does the content exist without JavaScript, does the main content start near the top of the HTML, is the page small enough to be fetched whole. It is the heaviest dimension because everything else is decoration if the crawler receives an empty shell.

**AI discoverability — 25%, 6 checks.** Whether AI clients are allowed in and can find their way around: does robots.txt exist, does it allow the AI search bots, is there a sitemap, does the site answer `Accept: text/markdown`, is there an llms.txt, is Content-Signal declared.

**Structured data — 20%, 5 checks.** Schema.org/JSON-LD, Open Graph, meta description, canonical URL, `lang` on `<html>`. What tells a machine what the page *is*, rather than what it says.

**Semantic HTML — 15%, 5 checks.** Heading hierarchy, `<article>` or `<main>`, the ratio of semantic elements to divs, alt text that describes something, div nesting depth. What extraction uses to decide which parts of the page are the page.

**Content efficiency — 10%, 4 checks.** Token reduction from HTML to Markdown, content-to-noise ratio, page weight, inline styles. How much of what you send is content.

## Why not every check counts the same

Every check carries an evidence tier next to its weight, and the tier is the thing that sets the weight:

- **proven** — a model provider documents it, or measurement demonstrates it. 3 of the 23.
- **plausible** — sound reasoning, consistent with how extraction pipelines work, but no vendor has confirmed it. 13 of the 23.
- **speculative** — the convention exists and the industry believes in it; nobody has shown any bot consumes it. 7 of the 23.

Weighted through to the final number, that puts roughly 32 of the 100 points on proven checks, 51 on plausible ones and 17 on speculative ones.

The three proven checks: the page renders without JavaScript, robots.txt allows the AI search bots, the page carries Schema.org markup. The first is the best-evidenced signal in this field — no major AI crawler executes JavaScript. The second is the only hard gate on the board: block a search bot and you are absent from that assistant.

At the other end sit llms.txt and Content-Signal, both shipped on a great many domains, neither confirmed as read by any vendor, with Google stating llms.txt has no effect. We still score them — absence of evidence is not evidence of absence — but at 10% of their dimension each instead of the 20% llms.txt used to carry. Content efficiency is speculative end to end: retrieval pipelines run Readability-style extraction before a model sees anything, so lean HTML probably buys less than the industry assumes.

That rebalance is what rubric v2 was. Under v1, "renders without JavaScript" was worth 6% of the final score while token reduction — which no vendor has ever mentioned — was worth 10%: the best-evidenced check on the board counted for less than a guess.

## The rubric has a version number

The scorer exports `RUBRIC_VERSION`, currently 2. v1 was the original weights, February 2026. v2 is the evidence rebalance, July 2026.

Without it, a score is not reproducible. You run an analysis and get 90. Six months later you get 85. Did your site regress, or did we move the goalposts? A certificate that does not name the ruler it was measured with is a number nobody can check.

We shipped exactly that mistake. The version existed in the scorer and travelled on the result object, but it never reached the database — so the leaderboard ranked scores against each other with no idea whether they had been measured the same way, v1 rows sitting above v2 rows as if the comparison meant something.

Migration `007_add_rubric_version_to_analyses.js` added the column, defaulting every existing row to 1. That default was not a guess: every row predated v2, so 1 is the fact. It also indexes `(rubric_version, score)` so a ranking can be scoped to one rubric. The timing was the load-bearing part — it had to land before the first v2 analysis was written, or new rows would have silently claimed to be v1.

## What we deliberately do not score

**A page, not a site.** An analysis reads one URL. robots.txt, sitemap and llms.txt are site-wide; everything else is that page and only that page. A well-built page on an otherwise weak site scores well, correctly.

**What a model actually did with you.** We cannot see whether ChatGPT quoted you, whether Claude built its answer from your page, whether you were cited at all. Nobody can see that from outside. Every check is a property of what you serve, not of what was made of it.

**Whether a crawler truly got in.** When we probe as an AI bot and something at the edge turns us away while robots.txt says we are welcome, we do not know which of the two a verified crawler meets. That check is capped at 60 — a pass with a caveat, not a failure — and the detail line says what happened. A number is a bad way to say "unknown".

Nothing here checks whether what you wrote is any good. A high score is a good prior: a machine can read the page and can tell what it is. It is not a promise of being cited.

## We run it on ourselves

agentready.md scores 94/100. That figure is read out of the database — our own most recent analysis of our own domain, linked to its certificate — rather than typed into the template. When it moves, the page moves. When there is no analysis, the page shows nothing rather than a stale number.

It has gone down before. For months our origin returned `404` for `/robots.txt` while Cloudflare's managed Content Signals Policy answered at the edge with a `200` and a file we had never written. Our own scorer read that `200` and passed us. When we fixed the origin and turned the managed file off, we went from 93 to 90. The lower number was the honest one — those three points had been measuring Cloudflare's file, not ours.

[How the score is built](/about) · [What everyone else scores](/leaderboard)
