Scoring rubric · v0.6.1
How the Answer-Engine Readiness Score is calculated
Eight checks, a published weight for each, and a label saying how well-founded each one is. Nothing here is a trade secret, and the parts that are our opinion are marked as our opinion.
Introduction
Why publish this
A number between 0 and 100 looks like a measurement. This one is not. It is a weighted opinion about what makes a page easy for an AI answer engine to quote, and an opinion is only worth acting on if you can see the reasoning.
So the whole rubric is here: every check, the exact thresholds that decide pass, warn and fail, the weight each one carries, and a label saying whether the check tracks something a search or AI vendor has published, something testing suggests, or something we simply think is a good bet.
Two things follow from that. You can argue with a specific check instead of with the score as a whole. And you can tell at a glance which parts of your score rest on documented behaviour and which rest on our judgement.
Mechanics
How the score is built
Each check returns one of three statuses. A pass earns its full weight, a warn earns half, a fail earns nothing. The total is divided by the sum of all weights and multiplied by 100:
score = round( sum( weight × factor ) / sum( weight ) × 100 )
factor: pass = 1.0, warn = 0.5, fail = 0.0The eight built-in weights add up to 100, so in a default install the weight column reads directly as points. The division still matters: the score is normalised against whatever checks are actually present, so adding or removing one through the filter rebalances the rest instead of pushing the maximum above 100.
What the scorer reads
The analysis reads the post content as it is stored, not the rendered output. Block markup has its delimiters stripped, classic content goes through wpautop, and shortcodes are unwrapped to the text inside them, up to five levels deep.
Running the full the_content chain would fire every shortcode, dynamic block and oEmbed on the site once per post, and a Dashboard scan reads up to fifty posts in one request. If your content lives outside post_content, as it does with most page builders, hand the scorer real markup through the flexa_seo_aeo/readiness/content_html filter.
Honesty
What the evidence levels mean
Every check carries one of three labels, shown next to it in the editor sidebar and on the Dashboard:
| Level | What it claims | What it does not claim |
|---|---|---|
| documented | The signal is described in a search or AI vendor’s own published guidance, so the check tracks a stated requirement. | That following it guarantees a citation, or that the weight we gave it is right. |
| observed | Third-party studies and our own testing associate the signal with being quoted. | That any engine documents it as a rule. Nobody has committed to it in writing. |
| heuristic | A reasoned bet. The cost of doing it is low and the upside is plausible. | That any engine reads it at all. This is the level where we could simply be wrong. |
The levels describe the signal, never the weight. Weights are editorial across the board, including on the two checks labelled documented. See where the weights come from.
The rubric
The eight checks
Weights are out of 100. Thresholds are exact: these are the numbers in the code, not a paraphrase of them.
| Check | Weight | Evidence |
|---|---|---|
| Structured data | 20 | documented |
| Meta description | 15 | documented |
| Answer-first opening | 15 | observed |
| Question-style headings | 15 | observed |
| FAQ / Q&A blocks | 10 | observed |
| Listed in llms.txt | 10 | heuristic |
| Markdown alternate | 10 | heuristic |
| Content depth | 5 | observed |
Structured data · 20 · documented
A site-wide check, not a per-post one. It passes when AEO Core → Structured data is on, which is what makes the post ship an Article or WebPage JSON-LD graph, and fails when it is off. There is no middle state and no warn, because the setting is a toggle. The fail carries a one-click site-wide fix.
Documented because the JSON-LD vocabulary and the engines’ expectations around it are published at length. Weighted highest because it is the only check that hands an engine facts in a form it does not have to infer from prose.
Meta description · 15 · documented
| Status | Condition |
|---|---|
| Pass | 50 to 160 characters inclusive |
| Warn | 1 to 49, 161 and over, or nothing at all |
| Fail | Never. This check cannot fail. |
The length is measured on the description the front end will actually emit: the one typed into the post if there is one, otherwise whatever your template resolves to. A post with no hand-written description can still pass on the template alone.
A page where nothing resolves scores zero for this check, and the row still reads warn rather than fail, because an empty description is usually not a fault in the content. A password-protected page is left unsummarised deliberately, in case the secret is in the body. A shortcode or page-builder layout reads as empty because the scan quotes plain text and does not render the page. Only the third case, a post with prose that simply has no sentence worth quoting yet, is something to go and write. The hint on the row names which of the three you are looking at.
The 50 to 160 band is a convention rather than a published limit, so the band itself is our call even though the signal is documented.
Answer-first opening · 15 · observed
| Status | Condition |
|---|---|
| Pass | First paragraph is 40 to 360 characters |
| Warn | 1 to 39, or 361 and over |
| Fail | No paragraph with any text in it |
The first paragraph that carries text, measured after tags are stripped and whitespace is collapsed. The idea is that a self-contained opening can be lifted as an answer without the engine having to assemble one.
Observed, not documented: engines favouring the lede is a pattern people have measured from the outside, not a rule anyone publishes.
Question-style headings · 15 · observed
| Status | Condition |
|---|---|
| Pass | Two or more question headings |
| Warn | Exactly one |
| Fail | None |
Only h1 through h4 are read. A heading counts as a question if it ends in a question mark, or if its first word is one of:
how what why when where which who
can does do is are should willThis check only understands English
The word list is English and is not translated. On a site written in another language, only headings that end in a question mark will be counted, so expect this check to under-report. If that describes your site, treat the 15 points as noise or replace the check through the filter below.
FAQ / Q&A blocks · 10 · observed
| Status | Condition |
|---|---|
| Pass | At least one core Details block is present |
| Warn | None found |
| Fail | Never. This check cannot fail. |
Detection looks for the core wp:details block, which is also what the plugin turns into FAQPage structured data. A third-party FAQ block will not be counted even when it renders something that looks identical, because the plugin cannot emit FAQPage markup for a shape it does not know how to read.
It warns rather than fails because plenty of good pages have no business carrying an FAQ.
Listed in llms.txt · 10 · heuristic
| Status | Condition |
|---|---|
| Pass | The post type is included and the post is not noindex |
| Warn | llms.txt is on, but this post is excluded (its type is off, or it is set to noindex) |
| Fail | AEO Core → llms.txt is off site-wide |
Heuristic, and worth being blunt about: no major answer engine has committed to reading llms.txt. It is a proposed convention. We score it because publishing the file costs nothing and the file is correct if the convention is ever adopted, not because we have evidence that anything consumes it today.
Markdown alternate · 10 · heuristic
| Status | Condition |
|---|---|
| Pass | Agent Readiness and the Markdown export are both on |
| Warn | Exactly one of the two is on |
| Fail | Neither is on |
Both halves are needed for the feature to do anything: one serves the Markdown rendering, the other advertises it through a Link header and a <link rel="alternate">. Serving it without advertising it means nothing finds it; advertising it without serving it means a crawler follows the link to nothing.
Heuristic for the same reason as llms.txt. Crawlers are free to prefer a Markdown alternate and some agents do, but no engine documents this as something it looks for.
Content depth · 5 · observed
| Status | Condition |
|---|---|
| Pass | 300 words or more |
| Warn | 100 to 299 |
| Fail | Under 100 |
Words are counted on the stripped text, split on whitespace. The lowest weight on the board, deliberately: word count is the easiest thing on this page to game and the weakest proxy for whether a page is worth quoting. It is here because very thin pages really do get skipped, not because longer is better.
Reading the number
Grades
The letter grade is a band on the same number. The first threshold a score meets wins:
| Grade | Score |
|---|---|
| Excellent | 85 and above |
| Good | 70 to 84 |
| Fair | 50 to 69 |
| Needs work | Under 50 |
Three of the eight checks are site-wide settings worth 40 points between them, so a site with structured data, llms.txt and the Markdown alternate all switched on starts every post at 40 before anyone writes a word. That is intentional, and it is also why a fresh install shows low scores everywhere until the settings are configured.
Honesty
Where the weights come from
They are our judgement. No engine publishes how it trades these signals off against each other, and we have not run an experiment that would let us derive them.
What the numbers actually encode is an ordering:
- Machine-readable facts beat prose, so structured data sits at the top on its own.
- The three signals about how the page is written (description, opening, headings) are treated as equally important, at 15 each.
- Things that are either a nice-to-have or an unproven bet sit at 10.
- Word count sits at 5 because it is the easiest to game.
Reasonable people will disagree with that ordering, and we would rather you argue with the table than with the number it produces. If you have data that contradicts any of it, we would like to see it.
Dashboard
The site-wide scores
The Dashboard shows three numbers, and only one of them is this rubric.
| Score | What it is |
|---|---|
| AEO | The mean of the per-post readiness scores across the posts in the scan, computed with the rubric on this page. |
| Technical SEO | A separate pass over site settings (titles and metas, XML sitemap, schema, Open Graph, Twitter cards, breadcrumbs, image alt text, IndexNow, llms.txt, commerce). These are on or off, not scored per post. |
| SEO | A roll-up over those same technical groups. |
The scan is capped, so the AEO number is a sample of recent content rather than a census of the whole site. Treat a change of a point or two between scans as noise.
Developers
Adding your own check
A theme or add-on can append rows to the checklist. The score renormalises, so your weight is relative to the eight built-ins rather than added on top of 100.
add_filter(
'flexa_seo_aeo/aeo/readiness_checks',
function ( array $checks, WP_Post $post ): array {
$checks[] = [
'id' => 'my_byline',
'label' => __( 'Author byline', 'my-theme' ),
'status' => has_byline( $post ) ? 'pass' : 'warn',
'weight' => 10,
'hint' => __( 'Named authors help engines attribute a quote.', 'my-theme' ),
'fix' => null,
'evidence' => 'observed',
];
return $checks;
},
10,
2
);| Key | Notes |
|---|---|
| id | Unique string. Used as the React key and for Dashboard tallies. |
| status | pass, warn or fail. Anything else scores as a fail. |
| weight | Any non-negative integer. Negative values are clamped to 0. |
| fix | Either null, or { label, scope, patch } where patch is a partial settings payload POSTed to /settings. |
| evidence | documented, observed or heuristic. Missing or unrecognised values are read as heuristic. |
You cannot invent a stronger label
An evidence value outside the three known levels is replaced with heuristic rather than passed through. The labels mean what this page says they mean, so a third-party check cannot ship a more authoritative-sounding one.
Feedback
If you disagree with a check
That is a useful bug report, and a more useful one than "the score seems wrong". The things worth telling us:
- Which check, and whether you are disputing the threshold, the weight, or the evidence level.
- What you would set it to, and what you are basing that on. A link to vendor documentation is enough to move a check from observed to documented.
- Whether a check is scoring your content wrongly rather than being wrong in principle. Those are different fixes.
The checks labelled heuristic are the ones most likely to be wrong, and we would rather retire one than keep scoring something nothing reads.