Rubric changelog
Rubric versions, what changed, and the corpus impact of each change.
Rubric changelog
v2.1 — 2026-10-02
Rubric v2.1 makes Compliance & Trust opt-in and scores it rule by rule (T7.55; owner
decision 8; AItoolsplan.md "How playbooks change the P audits"). Spec version unchanged (llms.txt
v2). Fairness and gaming review: docs/research/rubric-v2.1-review-2026-10-02.md. Before and after:
corpus/derived/rescore-2026-10-02-v2.1.md.
Rule changes.
- The gate is a declaration.
playbook_matchedis true when the covering llms.txt declares an
industry and a declared playbook can be scored. Syntax: one Industry: (or Industries:) line
before the first ## section (plain, > blockquote, list item or bold key), in ## Identity,
or in a section whose heading starts with "Compliance"; values separated by , ; | or +;
each a playbook slug or display name compared without case, spaces or punctuation
(Industry: Housing, General business). At most 3 (as in a playbook combination); further known
values are reported as over_limit, values we don't know as unknown; neither is scored. The
first such line counts; code fences are not read. Parsed as declared_industry (parse-core).
- Only signed-off rules score. A declared playbook is scored when it is
publishableand has a
signed-off (status: rule) blocking or high rule in a P1–P4 category with a matcher. Every
shipped playbook is still "to verify" today, so a declaration is recorded and reported ("its
playbook awaits reviewer sign-off") but the category stays Not applicable.
- The domain list is retired. v2.0 matched 7 homebuilders and brokerages by domain
(khov|tollbrothers|taylormorrison|tripointehomes|lgihomes|compass|opendoor → real-estate).
No domain opens the gate in v2.1.
- Profile. A scored industry declaration (an
industry-kind playbook) selectsregulated.
The REGULATED domain regex is kept as the fallback for sites that declare no scored industry:
retiring it would have moved 22 undeclared sites to another profile, 17 of them down (up to
−1.9, wegmans.com and metlife.com D → F), which would make not declaring cost points.
- P1–P4 measure coverage with a matcher per rule (
tools/playbook-matchers.mjs, 325 rules, every
blocking/high P1–P4 rule of every playbook, signed off or not), severity-weighted (blocking 2, high
1); P3 (and P1, P2, P4) Not applicable where the scored playbooks define no rule of that kind; P4
half credit for a trigger without a route. The phrase proxies (compliance_language,
never_confirm) no longer score anything (the parse flags stay for research).
- P5 reads a quoted or labelled not-advice disclaimer addressed to agents (was 0.7 for any
compliance phrase). P6 is measured (citation preference 0.5 + content licensing for AI use 0.5;
was a constant 0) and leaves the known-constants list (CONSTANT_SCORED is now R1, R2, R3).
- AGENTS.md counts. A rule is found in the llms.txt or AGENTS.md; each file is matched on its
own and the breakdown keeps each file's coverage (scans.audits.compliance) with the scored
rules on P1–P4 (rules: short name, severity, credit), so the report is a gap list.
- Draft re-score (T10.4).
score-drafttakes the playbooks: an llms.txt draft that declares or
drops an industry re-gates P1–P6 and the profile, recombined with the scan's AGENTS.md coverage;
an AGENTS.md draft re-checks P1–P6 against the scan's llms.txt coverage. A v2.0 scan is refused
(rubric_changed) as before, so no draft mixes two rubrics.
Fairness. Undeclared sites are scored exactly as v2.0 scored unmatched sites: the category is
removed and its weight redistributed, never zero. Declaring is opt-in to a stricter check: it can
lower a score until the file covers the playbook (simulated: the 7 former domain-list sites would
each score lower declared than undeclared today), which is the intended incentive (declare when
your file carries the rules), stated on the audit pages. The one site that loses points in this
rescore, www.khov.com (−0.9, stays C), lost credit it got from a domain list, not for not
declaring; it regains the category by declaring once the housing playbook is signed off.
Corpus impact (157 domains: the 156 of the published corpus plus www.khov.com, archived by a
live scan on 2026-07-29; corpus/derived/scores-2026-10-02-v2.0.ndjson → …-v2.1.ndjson, the
same parse, corpus/derived/parsed-2026-10-02.ndjson):
- No file declares an industry, so P1–P6 apply to none (v2.0: 7).
- Mean 57.63 → 58.09 (157); 57.51 → 57.98 over the 156. 6 up, 1 down, 150 unchanged. Grades
A/B/C/D/F 0/1/12/55/89 → 0/1/13/56/87: taylormorrison.com D → C (62.6 → 78.3),
tripointehomes.com F → D (50.9 → 63.6), compass.com F → D (49.4 → 61.7); opendoor.com +11.7,
lgihomes.com +10.8, tollbrothers.com +10.6 (all F). Each had a phrase-proxy compliance score of
0 at the regulated weight of 20. www.khov.com 75.9 → 75.0 (C): its proxy score was 79.5.
- No profile, cap or non-P audit changes anywhere.
v2.0 — 2026-09-28
Rubric v2.0 judges files against llms.txt v2 (llmstxt.org, modified 2026-08-10). Every
scorecard and breakdown now records spec_version: "llms.txt v2 (2026-08-10)" next to
rubric_version. Why: llmsupdate926.md. Interfaces: tools/RUBRIC-V2-CONTRACT.md.
Owner decisions. Numbered 2.0 (a break, aligned with the spec), not 1.2. E5 is retired from
scoring; capture of llms-full.txt / llms-ctx*.txt continues for research. The agent test
(A10) is parked until the paid tier. Subpath scans are allowed in the free tier, with the same
quota.
Rule changes.
- R1: any covering file is full credit. v2: "A file covers the URLs under its path, and where
more than one file applies, agents should use the most specific one." R1 is 1.0 for the most
specific covering file, root or path (was 0.5 at a subpath). Which file covers the scanned URL
is recorded in <date>.llms.meta.json and shown as R1's evidence and the scorecard's
evidence.covering_path; no meta file means the root file.
- `no_file` cap: only when no file covers the URL at its path or any ancestor, up to the
root. The scan decides this before scoring (the engine only runs on a file).
- S5 retired (
## Optionalpresent). v2 took away Optional's special meaning. Spec is now
S1–S4 at 25 each (were 20).
- C4 is budget only. A file over 8k tokens with
## Optionalno longer gets 0.85; it gets the
same budget score as any file its size.
- C8 (new, Curation, weight 20, gate: file has links): share of the file's links that point to
LLM-friendly content, meaning a .md/.txt URL, or a page declaring a markdown alternate.
C1–C7 scaled by 0.8 (to 16/16/8/12/12/8/8) to keep their ratios.
- E3: a page counts as having a markdown twin in either v2 form (
page.html.mdorpage.md;
index.html.md or index.md), declared alternates first. That is a change to the sampler; the
engine still scores E3 as the sampled hit rate.
- E5 retired (
llms-full.txt). v2 removed context expansion. - E8 (new, Ecosystem, weight 9, gate: pages checked): pages declare
rel="alternate" type="text/markdown" (0.5) and rel="describedby" (0.5), from <head> or the
Link: header, averaged over the homepage and up to 3 sampled pages that answered 200
(<date>.pages.json). A link to a markdown version samples the page it mirrors
(/docs/method.md → /docs/method), so a file that links only to .md URLs still gets its
pages checked. UNVERIFIED when no pages were checked. E8 takes E5's 9, so E1–E7 keep
their weights. The §4.6 table now matches the registry (22/18/18/13/9/11/9).
- WebMCP counts as a machine surface for A5/A6 (new parser flag
mentions_webmcp: "WebMCP",
navigator.modelContext, document.modelContext, toolname=).
- Self-blocking (R5, E1, `self_blocking` cap) follows RFC 9309. Before, a reader counted as
blocked only on a literal Disallow: / under the last User-agent line. Now it is blocked when
tools/robots.mjs isAllowed says it may not fetch the covering llms.txt. So consecutive
User-agent lines share their rules, an Allow: /llms.txt carve-out is honoured (not blocked),
Disallow: /llms.txt or a Disallow over the file's subpath is caught, and a reader with no
group of its own falls back to User-agent: *. User-agent: * + Disallow: / counts as
self-blocking: the file is unreadable to every reader, which is exactly what R5 ("AI crawlers
permitted to fetch it") and the cap describe. The reader set and the named count are
unchanged.
- Still constant (the §5 ratchet in
tools/test/score.test.mjs): R1, R2, R3, A4, A6, A9, P6.
R1 stays constant by design, since every covering file now earns 1.0. C8 and E8 launch measured,
and the ratchet asserts that both apply and vary.
Corpus impact (156 domains; the same capture as v1.1, which reproduces exactly from the v1.1
engine; corpus/derived/scores-2026-09-28-v2.0.ndjson against …-v1.1.ndjson):
- Mean total 60.42 → 57.96 (−2.46): 38 up, 101 down, 17 unchanged. Grades: 6 up, 31 down
(A/B/C/D/F 0/3/17/67/69 → 0/1/13/55/87). Most of the downward moves are D → F near 60.
- C8 accounts for the drop (mean −2.49 on its own). It applies to 145 files, with a mean
of 0.33: 75 have no .md/.txt links (0), 47 score ≥ 0.5 and 19 score 1.0. Docs sites that
link markdown gain (mintlify.com and datadoghq.com +4.5, platform.claude.com +4.3). Brand sites
linking HTML pages lose up to ~6 (siemens.com −6.3, target.com −6.2, kaggle.com −6.2).
retool.com and zapier.com fall from B to C.
- S5 and C4. S5 applied to 38 files and credited 8, so 30 long files without
## Optional
gain Spec points (Spec mean +2.75). C4 changes on the 8 files that had the escape: 3 up
(bun.sh, docs.perplexity.ai, together.ai: the budget alone scores above 0.85) and 5 down
(posthog.com to 0.3, docs.stripe.com to 0.55). Net effect on the mean: +0.27.
- E5. Applied to 38 files, credited 13. Net effect on the mean: +0.08.
- Self-blocking. 3 → 4 capped. The new one is khov-dev (www-dev.khov.com, the staging host):
User-agent: * / Disallow: / makes its file unreadable to every reader, so it falls from 72.8
to 25. The other three stay capped, and no site loses its cap: R5 and E1 change on khov-dev
only.
- R1, E8, WebMCP: no corpus effect yet. Every archived file came from
/llms.txtand none has
a llms.meta.json. No pages.json exists, so E8 is UNVERIFIED on all 156. The one file that
mentions WebMCP (zapier.com) was already credited for MCP.
- Rank agreement with the Edition 1 judgments (Kendall tau, n = 55, method of
tools/render-rankings.mjs): 0.374 → 0.335. Without khov-dev it is 0.369 → 0.364. Nearly all of
the fall is khov-dev, which Edition 1 judged on content (77) while v2.0 caps it for being
unreadable. Retiring S5 and the C4 escape costs 0.016 of the rest, because Edition 1's judges
worked from v1 and praised ## Optional. C8 adds 0.002 (a C8 weight of 10 would score 0.325).
Measurement fixes (2026-09-29). Two engine gaps found while writing the audit FAQs
(docs-accuracy.md); no weights changed, and rubric_version stays 2.0 (for A7.3–A7.5 too, by
owner decision 11.6). The only gate changes are E3's md_twins_sampled, which now receives a
sample, E4's jsonld_checked and E6's canonical_checked, which are now computed from the page
capture, and A6's has_actions and A4's multi_journey, which now read an action surface and
evidence of multi-step work (all below).
- A7 accepts `## FAQs`. The heading test was
/\bfaq\b|frequently asked/i, so a section
named "FAQs" scored 0.2; it is now /\bfaqs?\b|frequently asked/i.
- E1/R5 naming reads a versioned `User-agent`.
User-agent: GPTBot/1.0now names GPTBot:
the /… suffix is dropped before matching, as the RFC 9309 blocking rule (tools/robots.mjs)
already did, so naming and blocking read the same line the same way. The scan progress line
(worker/src/progress.mjs) follows suit and, like the engine, ignores a trailing # comment.
- Live scans fetch `/CLAUDE.md` and `/SKILL.md`. Live capture fetched only
/AGENTS.md, so a
site with only a CLAUDE.md scored 0 on E7 on a scan but 0.6 in the research corpus. It now
fetches all three, as corpus/capture.sh does, with the same acceptance rule (HTML, non-200 and
under 20 bytes rejected) and archives them under the same names. The scan progress line names
CLAUDE.md / SKILL.md only when found.
- Corpus impact: none (
node tools/audit-stats.mjs, 156 domains: every figure unchanged).
No archived file names a section "FAQs" (A7 full marks stay 5 of 156, 3.2%), and no archived
robots.txt puts a version on an AI crawler's User-agent (E1 full marks stay 30 of 148,
20.3%).
- Docs follow the engine. The per-audit docs and the §3.3 and §4 tables now state the
engine's actual formula and gate for every audit, marking the rubric's intended measure as
Planned where the engine uses a proxy or a constant.
- A URL without a trailing slash can be a directory. Resolution treated the last path
segment of https://x.com/docs as a page and never tried /docs/llms.txt. A last segment with
no file extension is now also tried as a directory, most specific first (/docs/guide tries
/docs/guide/llms.txt, /docs/llms.txt, /llms.txt); one with an extension (/docs/page.html)
stays a page. The cap of five candidates, root always last, is unchanged; a scan of an
extension-less deep URL makes at most one more request.
- 401 is turned away, not Unverified. A 401 on the llms.txt fetch means authentication is
required: the file is not publicly retrievable, which is a fact about access, like a 403, not a
measurement failure. The scan outcome is now BLOCKED ("turned away at the door", copy now
covers a login) instead of UNVERIFIED (worker/src/score-bridge.mjs classifyOutcome).
Neither outcome is scored, so no total changes; only the report the site sees does.
- A robots.txt 4xx means no rules (RFC 9309 §2.3.1.3). A robots.txt that answers 4xx is
"unavailable", and crawlers may access everything. R5 and E1 were UNVERIFIED for it; they now
score as a robots.txt with no rules: nothing blocked, no AI crawler named, so 0.5 each and no
self_blocking cap. The engine only does this when the capture recorded the 4xx: the scan
(worker/src/score-bridge.mjs) and corpus/capture.sh write <date>.robots.meta.json
({ url, status }), which robotsPolicy reads. No record, a 5xx, no response, or a 429 (a
rate limit on our request; Google also treats it as a server error) stays UNVERIFIED. When an
archive holds both, the newer day wins, and on the same day the file we read does.
- A short robots.txt is archived. The capture rule "a 200 under 20 bytes is empty" threw away
complete robots.txt files such as User-agent:* / Allow: (19 bytes). robots.txt now only
needs a 200 and a non-HTML body (worker/src/accept.mjs, corpus/capture.sh); an empty 200
robots.txt is also a file with no rules and scores 0.5 on R5 and E1.
- Markdown versions (E3) are measured, live and in the corpus (A5.1). E3 was UNVERIFIED on
every scan because nothing passed the twin sample to the scorer (mdTwins: null). The sampler
(tools/sample-md-twins.mjs sampleTwins) now runs on every live scan (worker/src/capture.mjs
fetchMdTwins, after the pages capture so declared alternates are tried first; LLMSRank-User,
like the rest of the scan) and writes <date>.md-twins.json, which the engine reads for E3
(tools/score.mjs mdTwinSample). Cost per scan: 3 pages in parallel, at most 4 candidate URLs
each (HEAD, a ranged GET only where HEAD is refused), 5 s per request, 10 s for the whole sample;
what the budget cuts off is unverified, never a miss. Scoring is found ÷ pages answered
definitively (200, 404, 410); a 403, 429, timeout or robots.txt block leaves the page out, and
zero definitive pages is UNVERIFIED. A file with no URLs at all is now NOT_APPLICABLE. The scan
progress line reads "markdown versions · 2 of 3 found".
- A linked page served as markdown is its own markdown version. A link such as
https://www.pinecone.io/product/md/ that answers text/markdown was scored a miss, because a
directory URL only tried index.html.md and index.md. The page capture (<date>.pages.json)
now records the content type of a non-HTML page, and a page that answered text/markdown or
text/plain counts itself first. No extra requests.
- Corpus impact of E3 (
node tools/audit-stats.mjs, 156 domains; twins sampled 2026-09-29 by
LLMSRankBot behind robots.txt, 6 sites at a time, corpus/files/<domain>/2026-09-29.md-twins.json
and corpus/derived/md-twins-2026-09-29.ndjson; declared alternates read from the page heads,
not archived as pages.json, so E8 stays as it was): E3 applies to 123 sites, is UNVERIFIED on 31
(17 with no same-site page links the parser reads, 14 where every check was blocked, rate-limited
or timed out) and NOT_APPLICABLE on 2. 42 of the 123 (34.1%) score 1.0, 70 (56.9%) score 0, mean
0.388. 22 hits came through a declared alternate or a page served as markdown. Totals: mean 58.0
→ 57.5, median 59.4 → 58.5; 44 up, 72 down, 40 unchanged; grades 10 up, 14 down (D 55 → 51, F 87
→ 91). The largest moves are developer.civitai.com +13.0 (F → D) and jasper.ai −4.3.
- Corpus impact of these three: none (
node tools/audit-stats.mjs, 156 domains: every figure
unchanged). No corpus llms.txt fetch recorded a 401 (<date>.meta.json http_status: 153
× 200, 4 × 403, 1 malformed), and the 2026-07-19 capture recorded no status for a robots.txt it didn't archive, so the
8 domains with no archived robots.txt stay UNVERIFIED on R5 and E1 until they are recaptured.
- JSON-LD agreement (E4) is measured (A5.2). E4 was UNVERIFIED on every scan because page
markup wasn't captured. The page capture that E8 already runs (tools/capture-pages.mjs, live
and corpus) now also records each page's identity JSON-LD (tools/jsonld.mjs extractJsonLd):
only @type, name, alternateName, legalName, url and sameAs, at most 8 entities per
page, stored in pages.json (jsonld, format in tools/RUBRIC-V2-CONTRACT.md). No extra
requests. The read still stops at </head> when the head carries identity JSON-LD; otherwise it
continues into the body, to </html>, within the same 256 KB cap, because many sites emit
JSON-LD there (43 of the 103 corpus sites with identity JSON-LD have it only in the body,
stripe.com and vercel.com among them): reading the head alone would have scored them 0.
<link> relations are still read from the head only. A block that doesn't parse counts as
absent and never fails the capture. Rule: 0.4 identity present + 0.4 a name agrees with the H1
+ 0.2 a url on the file's site (§4.6, with the reasons for the thresholds). The gate
(jsonld_checked) needs a page that answered 200 and was read for JSON-LD, and, for a 0, the
homepage read to the end. A pages.json captured before this change has no jsonld, so E4
stays UNVERIFIED for it while E8 still scores.
- Corpus impact of E4 and E8 (
node tools/audit-stats.mjs, 156 domains; pages captured
2026-09-29 by LLMSRankBot behind robots.txt, 6 sites at a time, one
corpus/files/<domain>/2026-09-29.pages.json per domain with an archived llms.txt, 157 written).
The corpus had no pages.json until now, so E8 is measured across the corpus for the first
time: it applies to 142 sites and is UNVERIFIED on 14 (every page answered 403 or 429, robots.txt
disallowed them, or nothing is archived). None earns full marks, 97 (68.3%) score 0 and 27 score
0.5; mean 0.129. 40 sites declare a text/markdown alternate on at least one page and 6 declare
describedby. The same captures feed C8's declared alternates: 12 sites move, C8 mean 0.329 →
0.338, full marks 19 → 20. E4 applies to 123 sites and is UNVERIFIED on 33 (the same 14, plus
19 whose homepage was cut at the 256 KB cap or didn't load and no page showed identity). 94 of
the 123 (76.4%) score 1.0, 20 (16.3%) score 0 and 9 are partial (5 × 0.8, 3 × 0.6, 1 × 0.4);
mean 0.815. Totals: E8 (with C8) alone takes the mean from 57.50 to 56.78 (125 sites down), E4
then adds 0.70 (97 up); together mean 57.50 → 57.48 (57.5 as published), median 58.5 → 58.3; 77
up, 53 down, 26 unchanged; grades 5 up and 5 down, so the counts are unchanged (B 1, C 13, D 51,
F 91). The largest moves are ollama.com +4.3, n8n.io +3.6 (F → D) and developer.civitai.com −5.6
(D → F).
- Canonical agreement (E6) is measured (A5.3). E6 was UNVERIFIED on every scan. The page
capture now also records each page's canonical (canonical: the head <link rel="canonical">,
else the Link: header, resolved to an absolute URL; tools/links.mjs canonicalOf), and
the engine compares it with the URL the llms.txt links (tools/canonical.mjs): 1 equal, 0.5 the
same page in another form, 0 a canonical elsewhere; pages with no canonical are left out (rule
and reasons in §4.6). No extra requests. No corpus recapture was needed: the 2026-09-29
pages.json already keeps every head <link> element (and the raw Link: header), so
canonicalOf derives the canonical from those, resolved against the page URL, and pages.json
stays write-once; only new captures (and live scans) store canonical. The one loss: a head with
more than 100 <link> elements is cut at 100 in the capture, so a canonical after the hundredth
is missed and the page is left out (18 of 502 corpus pages reach the cap).
- Corpus impact of E6 (
node tools/audit-stats.mjs, 156 domains, same 2026-09-29 captures).
E6 applies to 111 sites and is UNVERIFIED on 45: 14 where no page loaded (the sites E8 also
leaves out) and 31 where a page loaded but none of the sampled linked pages declared a canonical
(a homepage-only capture, or no rel=canonical). Of the 330 linked pages compared, 308 are equal, 1
is the same page in another form and 21 point elsewhere (dell.com's links keep tracking
parameters and pluralise a path; docs.windsurf.com links pages whose canonical is on docs.devin.ai;
suno.com/explore canonicalises to the homepage). 96 sites (86.5%) score 1.0, 3 (2.7%) score 0, 12
are partial; mean 0.928. Total movement: mean 57.48 → 57.94 (57.5 → 57.9 as published), median
58.3 → 59.1; 98 sites up, 8 down, 50 unchanged; grades 4 F → D and 2 D → C (F 91 → 87, D 51 → 53,
C 13 → 15; B 1). The largest moves are cox.com and americanexpress.com +1.5; the falls are
under 1 point and are sites whose linked pages all point elsewhere (decagon.ai and
docs.windsurf.com, −0.8; lmstudio.ai −0.7).
- Entity identity (A8) is measured from the file (A5.4). A8 scored 0.6 with a version or
date stamp and 0.2 without, so identity itself was never read and nothing could score above 0.6.
The parser (tools/parse-core.mjs identitySignals, synced to the browser copy) now sets four
flags from the file's prose, never its link lines or code: identity_statement,
alternate_names, identifiers and glossary. Rule: 0.4 an identity statement + 0.3
alternate names or identifiers + 0.3 a glossary; 0 when the file states none (rule and reasons
in §4.4). The version stamp left A8 (it stays in C7), the jargon gate was dropped (no
deterministic test for jargon), and page JSON-LD does not corroborate (E4 scores it, and in the
corpus the legal names it declares appear in files only incidentally). Deterministic, no model.
The corpus parse (corpus/derived/parsed-2026-07-19.ndjson) gains the four flags, computed by
the current parser from the same archived files; nothing else in it changes, and a fresh
parse-llms --all differs from the old output only in these flags.
- Precision of the A8 signals (every corpus match read by hand, 156 files): identity statement
20 files, all naming the file's own entity (3 name a subsidiary or project entity of it: K.
Hovnanian American Mortgage, LLC; Toll Northeast V Corp.; UBS Limited); alternate names 2 (both
former names: Cencora, HII); identifiers 11 (8 tickers, an NMLS number, "the official website
of", a labelled primary domain), no false positive; glossary 11 (4 with a glossary heading, 7 through a linked
glossary page only), no false positive. Near misses excluded by the rule: "also known as RAG" (a term, not the
entity), Vercel's ### aliases, DeepL's "Create a glossary" endpoint, Synthesia's
translation-glossary feature, "Key terms: Agile, Scrum…" keyword lists, link descriptions
naming a company ("Rockwell Automation, Inc."). Known misses, by design: identity stated only in
a link title or description, and glossary pages whose URL isn't /glossary (insurance-glossary).
- Corpus impact of A8 (
node tools/audit-stats.mjs, 156 domains). A8 still applies to all
156. Before: 16 × 0.6 (stamp), 140 × 0.2, mean 0.241. After: 35 sites (22.4%) score above 0 — 7 ×
0.7 (identity plus identifiers or former names: albemarle.com, cencora.com, expedia.com,
hii.com, t-mobile.com, tollbrothers.com, khov-dev), 1 × 0.6 (cognizant.com: ticker and glossary),
13 × 0.4 (identity only), 14 × 0.3 (a glossary or identifiers only); 121 (77.6%) score 0; none
earns full marks (no file pairs an identity statement with a glossary); mean 0.096. Totals: mean
57.94 → 57.53 (57.9 → 57.5 as published), median 59.1 → 58.4; 29 up, 118 down, 9 unchanged (all
capped); grades 6 down (developers.deepl.com C → D; docs.unstructured.io, docs.leonardo.ai,
dxc.com, wegmans.com and paypal.com D → F): B 1, C 15 → 14, D 53 → 49, F 87 → 92. The largest
moves are hii.com +1.8, expedia.com, albemarle.com and t-mobile.com +1.7, and the falls of sites
whose stamp earned 0.6 while Agent Readiness had few applicable audits (harley-davidson.com −4.1,
americanexpress.com −3.8).
- API error handling (A9) is measured from the file (A7.3). A9 was a constant 0.2 (min(0.6,
judged A5) with an LLM audit) wherever an API was documented. The parser now sets api_errors,
api_limits, api_auth and api_recovery, and A9 scores the parts found ÷ 4 (rule and reasons
in §4.4). The gate is unchanged (has_api) and the judged overlay is gone (owner decision 11.3).
The report line names each part: "API errors: documented; rate limits: documented; auth:
documented; recovery: not found".
- Action endpoints (A6) are measured, and A6 has its own gate (A7.4). A6 was a constant 0.7
gated on the API signal, so a dentist's booking link never counted and an API-only docs site
always did. The gate is now an action surface (has_actions: api_write_operation,
mcp_endpoint, mentions_webmcp, action_link, action_prose), and the score is 0.5 for the
action plus 0.5 for guidance on when to offer it (offer_guidance). The report line lists the
surfaces found and whether the file says when to offer them.
- Journey flows (A4) are measured, and A4's gate asks for multi-step evidence (A7.5). A4 was
0.8 wherever intent-routing wording appeared (0.8 × judged A3 with an LLM audit). The parser
now counts flows (journey_flows) and whether one links its steps (journey_linked_steps) or
states a prerequisite or handoff (journey_prereq_handoff); A4 scores 0.4/0.6 + 0.2 + 0.2. The
gate is an agent-callable action or a flow (owner decision 11.2). The judged overlay is gone
(11.3). Deterministic, no model, for all three.
- Still constant (the §5 ratchet in
tools/test/score.test.mjs, mirrored by the app's
CONSTANT_SCORED): R1, R2, R3, P6. A4, A6 and A9 left the list, and the ratchet now also fails
if any of the three stops varying across the corpus files.
- Corpus parse.
corpus/derived/parsed-2026-07-19.ndjsongains the 12 new flags, computed by the
current parser from the same archived files, plus mentions_webmcp, which the parser has set
since v2.0 but this file predated: A6's and A4's gates read it, and zapier.com's only
agent-callable surface is its WebMCP manifest. Every other field is unchanged (each record,
with these flags removed, is byte-identical to the previous line). No other audit reads
mentions_webmcp differently: zapier.com, the one file that mentions WebMCP, already had MCP.
- Corpus impact of A9, A6 and A4 (
node tools/audit-stats.mjs, 156 domains, with the A7.10
fixes below; at A7.5 every per-site total equalled the frozen estimate in
docs/research/a4-a6-a9-patterns-2026-09-29.md). A9 still applies to 74: 36 × 0, 16 ×
0.25, 13 × 0.5, 9 × 0.75, none at 1.0; mean 0.2 → 0.233. Parts: auth 26, errors 22, limits 12,
recovery 9. A6 applies to 83 (was 74; 88 before the A7.10 fixes): 45 stay, 38 newly apply,
29 leave (API docs with no action surface, such as developers.cloudflare.com and github.com, and
the 5 whose only surface is a docs-search or read-only MCP server). Surfaces: 31 agent-callable
(23 API write operations, 8 declared MCP endpoints that take actions, 1 WebMCP; 6 more files
declare only a docs-search or read-only MCP server) and 57 user-facing (49 action links, 11
prose actions; 34 files have contact links only). 74 × 0.5 and 9 × 1.0 (aflac.com, attio.com,
docs.stripe.com, expedia.com, khov-dev, mccormick.com, ollama.com, trivago.com,
wyndhamhotels.com); mean 0.7 → 0.554. A4 applies to 35 (was 1; 40 before the A7.10 fixes):
before the Quickstart partial credit (owner decision 2026-10-01, below), 28 × 0, 5 × 0.4
(attio.com, carvana.com, firecrawl.dev, lmstudio.ai, trychroma.com), 1 × 0.8 (mccormick.com),
1 × 1.0 (khov-dev); mean 0.8 → 0.109. With it, 17 of the 28 score 0.2 and 11 stay at 0; A4's
mean is 0.206, the mean total 57.51 (57.5 as published) and the median 58.8, grades unchanged.
The figures that follow are before it. On its own (frozen estimate, before A7.10), A9 moves
the mean total 57.53 → 57.55, A6 → 57.60 and A4 → 57.34. Together: mean 57.53 → 57.45 (57.5 as
published; 57.44 at A7.5), median 58.4 → 58.55 (58.6 as published then); 40 up, 65
down, 51 unchanged; grades 8 up and 8 down (B 1, C 14 → 11, D 49 → 55, F 92 → 89). Up to D:
dxc.com, westerndigital.com, wegmans.com, mccormick.com, paypal.com, albemarle.com, lumen.com,
paychex.com. Down: apify.com, clerk.com and docs.poolside.ai (C → D); elevenlabs.io,
posthog.com, pinecone.io, browserbase.com and ziprecruiter.com (D → F). The largest falls are
API sites with no flows and no offer guidance (developers.cloudflare.com and
developers.llamaindex.ai −2.7, prisma.io −2.4, augmentcode.com −2.3); the largest gains are sites
with action links, offer guidance or a linked flow (mccormick.com +2.8; sentry.io, mistral.ai,
wyndhamhotels.com and aflac.com +2.6; expedia.com +2.4).
- A7.10 fixes (gaming and fairness review, AIplan.md item 11). Five rule changes, each with
the review's crafted gaming file as a test: (1) a docs-search or read-only MCP server (its line
mentions /_mcp/server, an mcp-docs. host, "docs MCP" or documentation, or its H2 section
says "read-only") still counts as declared and for A5, but no longer opens A6 or A4
(mcp_docs_only, a new corpus parse flag; every other field of each record is unchanged); (2) an
A4 flow needs more than half its steps with ≥ 2 words or a link or operation, and an arrow chain
needs its intro word and a colon before the first arrow; (3) an A6 guidance heading needs a body
and the same verb test as prose; (4) A9 recovery prose needs an idempotency key, not the bare
word; (5) an A9 heading's body needs a link, URL, code or a valued prose line. Fixes 2–5 change
no corpus figure. Fix 1: A4 applies 40 → 35 (mean 0.095 → 0.109), A6 88 → 83 (0.551 → 0.554),
mean total 57.44 → 57.45, median unchanged; dev.hume.ai 68.9 → 70.2 (D → C), you.com 74.4 →
75.5, assemblyai.com and docs.docker.com −0.1, ziprecruiter.com unchanged (its A6 0.5 and A4 0
cancel); grades C 10 → 11, D 56 → 55. Fern's generated "connect to the MCP server at
…/_mcp/server" line, which dev.hume.ai, developers.deepgram.com and you.com share, had cost
dev.hume.ai its C. llmsrank.dev's own file scores the same under all five (A4 0, A6 1.0, A9 0.5).
- Owner decision 2026-10-01: Quickstart partial credit. The A7.10 review measured, and didn't
recommend, 0.2 pointer credit for a linked Quickstart page when no flow is written (AIplan.md
item 11, decision 11.8); the owner chose it. When A4 applies and journey_flows is 0, a link
line whose whole normalised title is "quickstart", "quick start", "getting started" or "get
started" (optionally + "guide" or "tutorial"; ≤ 120 characters; a page, not an in-file anchor)
scores 0.2 (journey_quickstart_link, a new corpus parse flag; every other field of each record
is unchanged). The rule is tighter than the review's (any list-item title containing the words,
23 files): whole titles only, so "Getting started with feature flags", "W&B quickstart" and
"API quickstart" don't count. It never adds to a flow's score and never opens the gate. Report
line: "no numbered or arrow flow with a journey intro; a quickstart or getting-started page is
linked (partial credit)". Corpus: A4 applies to 35, unchanged; 17 move 0 → 0.2 (11 × 0, 17 ×
0.2, 5 × 0.4, 1 × 0.8, 1 × 1.0); A4 mean 0.109 → 0.206 (zero rate 0.80 → 0.31). Mean total
57.45 → 57.51, median 58.55 → 58.8; no grade changes (B 1, C 11, D 55, F 89). 14 totals move,
+0.5 to +0.8: mintlify.com 72.4 → 73.0, developers.deepl.com 68.1 → 68.8, developers.deepgram.com
67.9 → 68.5, dev.writer.com 66.9 → 67.4, docs.perplexity.ai 66.4 → 67.0, docs.cartesia.ai 61.4 →
62.0, inference-docs.cerebras.ai 61.3 → 62.0, docs.fireworks.ai 61.3 → 61.9, weaviate.io 60.2 →
60.9, docs.unstructured.io 58.2 → 59.0, docs.leonardo.ai 58.2 → 58.8, docs.langchain.com 57.3 →
58.1, vercel.com 54.5 → 55.1, platform.claude.com 49.1 → 49.7. algolia.com, twilio.com and
together.ai gain A4 0.2 but stay at their caps (30, 30, 25). llmsrank.dev's own file has two
flows and scores the same (A4 1.0).
v1.1 — 2026-09-28
Measurement fixes from updatesuggestions.md; no weights changed.
- C6 is measured. It was a constant 0.6. It now uses the archived sitemap per §4.3 and has
its own gate (sitemap_locs), so a sitemap we can't read completely makes C6 UNVERIFIED
without switching off E2. Sitemaps are captured by tools/capture-sitemaps.mjs.
- C6 doesn't apply below 50 sitemap pages. On a small site a complete file and a sitemap
mirror look the same (lumalabs.ai: 23 pages, 18 linked), so C6 is NOT_APPLICABLE there.
- E2 is measured. It was a constant 0.9 for "present". Now: 0.4 for a readable sitemap
+ 0.3 × share of pages with a lastmod + 0.3 × share of those that are plausible (a valid
W3C date, not after the capture day, and not one timestamp on ≥ 90% of a 10+ page sitemap,
which is a build time rather than an edit date). Index children that were archived count
even when the index is incomplete. No readable entries = UNVERIFIED.
- E7 needs a real file.
AGENTS.mdorCLAUDE.mdunder 200 bytes no longer earns credit. - Corpus impact (156 domains, sitemaps captured 2026-09-28): C6 applies to 98 sites, is
NOT_APPLICABLE on 2 and UNVERIFIED on 56; the sites it scores down are mostly docs sites whose
llms.txt lists ~80–98% of their sitemap. E2 applies to 130 sites, mean 0.84: 61 score 1.0 (60 as first published; the same capture
and engine reproduce 61, corrected 2026-09-29) and
24 with no lastmod score 0.4. Against v1.0 the mean moves +2.26 (115 up, 6 down; 24 grades
up, 1 down), and rank agreement with the Edition 1 judgments goes 0.309 → 0.374.
- Grades follow the stored total. The engine stored totals to one decimal but graded the unrounded value, so an unrounded 59.96 was stored as 60.0 with an F. It now grades the stored total. Corpus impact: 2 domains (dell.com, developers.llamaindex.ai) move from F to D; no score changes.
- AI crawler list. R5/E1 recognise 23 AI crawler and control tokens (
NAMED_TOKENSintools/ai-crawlers.mjs), up from 13: 21 from the vendors' own documentation, each with its source, plus Bytespider and cohere-ai, kept because they are common in robots.txt files although no vendor page documents them. R5/E1 also ignore trailing#comments on User-agent lines (RFC 9309). The self-blocking set is unchanged. Corpus impact: none (every site naming a newer token already named an older one). - Still constant (tracked in
tools/test/score.test.mjs, which fails if the list grows):
R1, R2, R3, A4, A6, A9, P6.
- `robots_archived` gate fixed (D0.5). A missing or unreadable robots.txt made R5 and E1
NOT_APPLICABLE; §3.1 says a measurement we couldn't make is UNVERIFIED. Gate is now
robots.state === OK ? true : UV. Corpus impact (156 domains, same capture): 8 domains
(algolia.com, developer.civitai.com, docs.synthesia.io, gm.com, kaggle.com, ollama.com,
qdrant.tech, t-mobile.com — the ones with no archived robots.txt) move R5 and E1 from
NOT_APPLICABLE to UNVERIFIED; total is unchanged for all of them (mean Δ = 0), since both
states drop the audit and redistribute its weight the same way — only the NA/UV counts shown
in the report change.