{
  "url": "https://besthumanizer.app",
  "finalUrl": "https://besthumanizer.app/",
  "scannedAt": "2026-11-11T23:07:49.598Z",
  "engine": "ARI-100",
  "score": 51,
  "core": 48,
  "bonus": 3,
  "bonusSignals": [
    "ard",
    "agent-skills",
    "llms-full"
  ],
  "grade": "D",
  "gradeLabel": "Largely invisible",
  "level": 0,
  "accessScore": 100,
  "accessCoverage": {
    "tested": 21,
    "of": 21
  },
  "a11yScore": 89,
  "a11yReport": {
    "score": 89,
    "checks": {
      "img-alt": {
        "result": "not_applicable",
        "note": "0/0 missing alt"
      },
      "form-label": {
        "result": "not_applicable",
        "note": "0 unlabelled"
      },
      "control-name": {
        "result": "not_applicable",
        "note": "see structure check"
      },
      "media-dims": {
        "result": "not_applicable",
        "note": "0 without dimensions"
      },
      "aria-refs": {
        "result": "not_applicable",
        "note": ""
      },
      "iframe-title": {
        "result": "not_applicable",
        "note": "0 iframes"
      },
      "heading-levels": {
        "result": "not_applicable",
        "note": "0 skipped"
      },
      "tabindex": {
        "result": "pass",
        "note": ""
      },
      "zoom": {
        "result": "pass",
        "note": ""
      },
      "doc-title": {
        "result": "pass",
        "note": ""
      },
      "label-in-name": {
        "result": "pass",
        "note": ""
      },
      "skip-link": {
        "result": "fail",
        "note": ""
      },
      "lang-valid": {
        "result": "pass",
        "note": ""
      },
      "aria-hidden-focus": {
        "result": "pass",
        "note": ""
      },
      "nested-interactive": {
        "result": "pass",
        "note": ""
      },
      "empty-heading": {
        "result": "not_applicable",
        "note": ""
      },
      "meta-refresh": {
        "result": "pass",
        "note": ""
      },
      "autocomplete": {
        "result": "not_applicable",
        "note": ""
      },
      "table-headers": {
        "result": "not_applicable",
        "note": ""
      },
      "svg-title": {
        "result": "not_applicable",
        "note": ""
      },
      "captions": {
        "result": "not_applicable",
        "note": ""
      },
      "list-structure": {
        "result": "not_applicable",
        "note": ""
      },
      "aria-valid": {
        "result": "not_applicable",
        "note": "0 invalid roles"
      }
    },
    "applicable": 9,
    "total": 23
  },
  "domains": [
    {
      "id": "access",
      "name": "Reachability & Access",
      "weight": 20,
      "blurb": "A crawler that is turned away at the edge reads nothing you wrote.",
      "score": 82.2,
      "checks": 15,
      "passed": 9,
      "applicable": 15,
      "total": 15
    },
    {
      "id": "render",
      "name": "Render & Delivery",
      "weight": 10,
      "blurb": "Text that only exists after JavaScript runs does not exist for most agents.",
      "score": 44.2,
      "checks": 10,
      "passed": 4,
      "applicable": 10,
      "total": 11
    },
    {
      "id": "structure",
      "name": "Structure & Semantics",
      "weight": 12,
      "blurb": "Headings, titles and landmarks are the outline an agent extracts passages from.",
      "score": 69.2,
      "checks": 12,
      "passed": 8,
      "applicable": 12,
      "total": 14
    },
    {
      "id": "data",
      "name": "Structured Data",
      "weight": 12,
      "blurb": "Facts declared in JSON-LD do not have to be inferred from prose.",
      "score": 15.8,
      "checks": 7,
      "passed": 1,
      "applicable": 7,
      "total": 12
    },
    {
      "id": "discovery",
      "name": "Discovery Files",
      "weight": 14,
      "blurb": "The files an agent reads before it reads a page.",
      "score": 46.8,
      "checks": 18,
      "passed": 6,
      "applicable": 18,
      "total": 18
    },
    {
      "id": "interfaces",
      "name": "Agent Interfaces",
      "weight": 14,
      "blurb": "What an agent can call once it decides to act.",
      "score": 53.4,
      "checks": 26,
      "passed": 11,
      "applicable": 26,
      "total": 27
    },
    {
      "id": "commerce",
      "name": "Operability & Commerce",
      "weight": 10,
      "blurb": "Real controls, real prices, real checkout — for agents that buy.",
      "score": 0,
      "checks": 1,
      "passed": 0,
      "applicable": 1,
      "total": 13
    },
    {
      "id": "trust",
      "name": "Trust & Authority",
      "weight": 8,
      "blurb": "Who said it, when, and can the claim be checked.",
      "score": 41.7,
      "checks": 10,
      "passed": 3,
      "applicable": 10,
      "total": 10
    }
  ],
  "checks": [
    {
      "id": "robots-valid",
      "domain": "access",
      "weight": 3,
      "tier": "essential",
      "title": "robots.txt is reachable and valid",
      "how": "GET /robots.txt returns 200, text/plain, and at least one User-agent group with parseable rules.",
      "fix": "Fix robots.txt at {{origin}}/robots.txt: it must return HTTP 200 with Content-Type text/plain, contain at least one \"User-agent: *\" group, and contain no HTML. Current evidence: {{evidence}}.",
      "result": "fail",
      "evidence": "robots.txt HTTP 200, 0 groups",
      "points": 0
    },
    {
      "id": "ai-search-bots",
      "domain": "access",
      "weight": 5,
      "tier": "essential",
      "title": "AI search crawlers are allowed",
      "how": "Live request as OAI-SearchBot, Claude-SearchBot and PerplexityBot all return 200 (not 403/challenge/empty).",
      "fix": "Unblock the AI search crawlers on {{origin}}. In robots.txt add a group with User-agent lines for OAI-SearchBot, Claude-SearchBot, PerplexityBot and \"Allow: /\", then confirm the CDN/WAF does not challenge them. Current evidence: {{evidence}}.",
      "result": "pass",
      "evidence": "11/11 verifiable AI search crawlers allowed",
      "points": 1
    },
    {
      "id": "ai-user-agents",
      "domain": "access",
      "weight": 4,
      "tier": "essential",
      "title": "Live AI agents can fetch a page a person asked about",
      "how": "Live request as ChatGPT-User, Claude-User and Perplexity-User return 200.",
      "fix": "Allow user-initiated AI agents on {{origin}}: add ChatGPT-User, Claude-User and Perplexity-User to the allowed robots group and remove any firewall rule keyed on those user agents. Evidence: {{evidence}}.",
      "result": "pass",
      "evidence": "8/8 verifiable live AI agents allowed",
      "points": 1
    },
    {
      "id": "search-engines",
      "domain": "access",
      "weight": 5,
      "tier": "essential",
      "title": "Classic search engines are allowed",
      "how": "Live request as Googlebot and Bingbot return 200.",
      "fix": "Allow Googlebot and Bingbot for {{origin}} in robots.txt and at the edge — AI answer surfaces are fed by these indexes. Evidence: {{evidence}}.",
      "result": "pass",
      "evidence": "allowed: Googlebot, bingbot",
      "points": 1
    },
    {
      "id": "no-edge-block",
      "domain": "access",
      "weight": 5,
      "tier": "essential",
      "title": "The edge does not challenge AI crawlers",
      "how": "A request carrying an AI user agent gets a 200 with comparable body size to a browser request; no \"cf-mitigated\", JS challenge or 403.",
      "fix": "Stop the CDN/WAF on {{origin}} from challenging AI crawlers: add an allow rule for the AI user agents ahead of managed bot protection, and make sure the challenge response is not being served instead of HTML. Evidence: {{evidence}}.",
      "result": "pass",
      "evidence": "no AI crawler was challenged",
      "points": 1
    },
    {
      "id": "no-blanket-disallow",
      "domain": "access",
      "weight": 3,
      "tier": "essential",
      "title": "No blanket Disallow: / applies to everyone",
      "how": "The wildcard group does not disallow the whole site, or a wildcard-by-path rule does not shadow the AI groups.",
      "fix": "Remove \"User-agent: * / Disallow: /\" from robots.txt on {{origin}}, or scope it to private paths only (/cart, /checkout, /account, /admin). Evidence: {{evidence}}.",
      "result": "pass",
      "evidence": "wildcard group does not disallow the site",
      "points": 1
    },
    {
      "id": "bot-policy-deliberate",
      "domain": "access",
      "weight": 2,
      "tier": "recommended",
      "title": "AI bot policy is named, not inherited",
      "how": "robots.txt names at least 6 known AI crawlers in explicit groups.",
      "fix": "Name the AI crawlers explicitly in robots.txt for {{origin}} instead of relying on a wildcard, so each one has a deliberate policy. Evidence: {{evidence}}.",
      "result": "fail",
      "evidence": "0 named user-agent groups",
      "points": 0
    },
    {
      "id": "training-policy",
      "domain": "access",
      "weight": 1,
      "tier": "recommended",
      "title": "Training policy is explicit",
      "how": "A stated allow/block for training-only crawlers (CCBot, Google-Extended, Bytespider) or a Content-Signal line.",
      "fix": "State a training policy on {{origin}}: either name CCBot / Google-Extended / Bytespider explicitly in robots.txt, or add a Content-Signal line (search=yes, ai-input=yes, ai-train=no). Evidence: {{evidence}}.",
      "result": "fail",
      "evidence": "no explicit training policy",
      "points": 0
    },
    {
      "id": "no-captcha",
      "domain": "access",
      "weight": 3,
      "tier": "essential",
      "title": "No CAPTCHA on primary pages",
      "how": "Homepage and sampled inner pages contain no visible CAPTCHA widget or captcha JS gate on first paint.",
      "fix": "Remove the visible CAPTCHA from {{url}}; agents stop at them. Keep challenges on login/contact forms only. Evidence: {{evidence}}.",
      "result": "pass",
      "evidence": "no CAPTCHA on first paint",
      "points": 1
    },
    {
      "id": "no-gate",
      "domain": "access",
      "weight": 3,
      "tier": "essential",
      "title": "No age/consent/geo wall in front of content",
      "how": "Server HTML contains the real page content, not an interstitial; no IP-based redirect to another locale on first hit.",
      "fix": "Serve real content from {{url}} in the server HTML and overlay any age/consent gate client-side; serve a default region at / instead of a geo redirect. Evidence: {{evidence}}.",
      "result": "pass",
      "evidence": "content is not behind an interstitial",
      "points": 1
    },
    {
      "id": "indexable",
      "domain": "access",
      "weight": 5,
      "tier": "essential",
      "title": "Page is indexable",
      "how": "No noindex in meta robots or X-Robots-Tag, no nofollow on the canonical.",
      "fix": "Remove noindex from {{url}}: drop it from the meta robots tag and the X-Robots-Tag header. Evidence: {{evidence}}.",
      "result": "pass",
      "evidence": "indexable",
      "points": 1
    },
    {
      "id": "snippets-allowed",
      "domain": "access",
      "weight": 2,
      "tier": "essential",
      "title": "Snippets and large image previews are allowed",
      "how": "meta robots or X-Robots-Tag permits snippets (max-snippet >= 0) and max-image-preview:large.",
      "fix": "On {{origin}} set \"max-snippet:-1, max-image-preview:large\" in the meta robots tag or X-Robots-Tag so assistants can quote the page. Evidence: {{evidence}}.",
      "result": "partial",
      "evidence": "snippet directives incomplete",
      "points": 0.5
    },
    {
      "id": "robots-coherent",
      "domain": "access",
      "weight": 2,
      "tier": "recommended",
      "title": "robots.txt does not disallow what the site advertises",
      "how": "No sitemap URL, discovery file, or manifest the site publishes is disallowed for the crawler that needs it.",
      "fix": "On {{origin}} make robots.txt consistent with the files you publish: allow /llms.txt, /sitemap.xml and every /.well-known/ endpoint you advertise. Evidence: {{evidence}}.",
      "result": "pass",
      "evidence": "declared resources are crawlable",
      "points": 1
    },
    {
      "id": "crawl-budget",
      "domain": "access",
      "weight": 1,
      "tier": "recommended",
      "title": "Infrastructure paths are excluded from crawl budget",
      "how": "/api/, /cdn-cgi/ and parameter permutations are disallowed; content and static assets are allowed.",
      "fix": "On {{origin}} exclude infrastructure from crawl budget: Disallow /api/ and /cdn-cgi/, then re-Allow /static/ and your badge/sitemap paths. Evidence: {{evidence}}.",
      "result": "partial",
      "evidence": "no /api/ or /cdn-cgi/ exclusion",
      "points": 0.5
    },
    {
      "id": "ssr-home",
      "domain": "render",
      "weight": 5,
      "tier": "essential",
      "title": "Homepage content is in the raw HTML",
      "how": "At least 500 characters of meaningful text and at least one H1 appear in the response body with scripts stripped.",
      "fix": "Server-render the homepage of {{origin}}: ship at least 500 characters of real text and an H1 in the initial HTML response rather than after hydration (SSR/SSG, or prerender). Evidence: {{evidence}}.",
      "result": "fail",
      "evidence": "55 chars of text, 0 H1 in raw HTML",
      "points": 0
    },
    {
      "id": "ssr-inner",
      "domain": "render",
      "weight": 4,
      "tier": "essential",
      "title": "Inner pages carry text without JavaScript",
      "how": "Sampled inner pages each return 400+ characters of text with scripts stripped.",
      "fix": "Server-render the inner pages of {{origin}} ({{evidence}}): move the primary copy into the HTML response for each template, not only the homepage.",
      "result": "not_applicable",
      "evidence": "no inner pages sampled",
      "points": null
    },
    {
      "id": "status-200",
      "domain": "render",
      "weight": 4,
      "tier": "essential",
      "title": "Canonical URL answers 200 within one hop",
      "how": "The scanned URL returns 200 after at most one redirect.",
      "fix": "Collapse redirects on {{url}}: aim for 0-1 hops to the final 200 URL and link the final URL directly. Evidence: {{evidence}}.",
      "result": "pass",
      "evidence": "HTTP 200, 0 redirect(s)",
      "points": 1
    },
    {
      "id": "real-404",
      "domain": "render",
      "weight": 3,
      "tier": "essential",
      "title": "Unknown URLs return a real 404",
      "how": "A made-up path returns 404 (not 200, not a soft 404 page).",
      "fix": "Return a real 404 status for unknown paths on {{origin}} instead of a 200 soft-404 page, and include links to real pages in the body. Evidence: {{evidence}}.",
      "result": "fail",
      "evidence": "unknown path returned 200 (soft 404)",
      "points": 0
    },
    {
      "id": "content-type",
      "domain": "render",
      "weight": 2,
      "tier": "essential",
      "title": "Correct Content-Type and charset",
      "how": "text/html; charset=utf-8 on HTML, text/plain for llms.txt and robots.txt.",
      "fix": "Set correct Content-Type headers on {{origin}}: text/html; charset=utf-8 for pages, text/plain; charset=utf-8 for robots.txt, llms.txt and markdown mirrors. Evidence: {{evidence}}.",
      "result": "partial",
      "evidence": "content-type text/html",
      "points": 0.5
    },
    {
      "id": "ttfb",
      "domain": "render",
      "weight": 3,
      "tier": "recommended",
      "title": "Response is fast enough for an agent timeout",
      "how": "Time to first byte under 800 ms and full body under 3 s from a cold request.",
      "fix": "Speed up the server response for {{url}}: TTFB should land under 800 ms. Cache the HTML at the edge, and remove synchronous calls from the render path. Evidence: {{evidence}}.",
      "result": "pass",
      "evidence": "76 ms to first byte",
      "points": 1
    },
    {
      "id": "page-weight",
      "domain": "render",
      "weight": 2,
      "tier": "recommended",
      "title": "Page weight is reasonable",
      "how": "HTML document under 1 MB and no unbounded inline payloads.",
      "fix": "Reduce the HTML payload of {{url}} to under 1 MB: move large inline JSON/state blobs and base64 images out of the document. Evidence: {{evidence}}.",
      "result": "pass",
      "evidence": "2 KB of HTML",
      "points": 1
    },
    {
      "id": "compression",
      "domain": "render",
      "weight": 1,
      "tier": "recommended",
      "title": "Text responses are compressed",
      "how": "br or gzip accepted and returned for HTML/JSON.",
      "fix": "Enable Brotli/gzip compression for HTML and JSON on {{origin}}. Evidence: {{evidence}}.",
      "result": "partial",
      "evidence": "no compression header relayed",
      "points": 0.5
    },
    {
      "id": "text-ratio",
      "domain": "render",
      "weight": 2,
      "tier": "recommended",
      "title": "Text-to-markup ratio is workable",
      "how": "Visible text / HTML bytes above 5% after stripping scripts, styles, nav and footer.",
      "fix": "Raise the text-to-markup ratio on {{url}} to at least 5%: trim wrapper divs and inline styles, and put the copy in plain elements. Evidence: {{evidence}}.",
      "result": "fail",
      "evidence": "text/markup 2.8%",
      "points": 0
    },
    {
      "id": "no-js-only",
      "domain": "render",
      "weight": 3,
      "tier": "essential",
      "title": "Primary content does not depend on client rendering",
      "how": "The page text is identical with JavaScript disabled at the HTML level (no empty root node).",
      "fix": "Remove the JavaScript dependency for primary content on {{origin}}: the raw response must not be an empty mount node. Render on the server and hydrate. Evidence: {{evidence}}.",
      "result": "fail",
      "evidence": "empty mount node detected",
      "points": 0
    },
    {
      "id": "streaming-stable",
      "domain": "render",
      "weight": 1,
      "tier": "recommended",
      "title": "Document is parseable without executing scripts",
      "how": "No document.write after load, no content injected above the fold on a timer.",
      "fix": "On {{origin}} stop injecting above-the-fold content after load (no document.write, no delayed hero render), so a non-executing parser sees the final DOM. Evidence: {{evidence}}.",
      "result": "pass",
      "evidence": "no blocking document.write",
      "points": 1
    },
    {
      "id": "title",
      "domain": "structure",
      "weight": 3,
      "tier": "essential",
      "title": "Title is unique and the right length",
      "how": "A <title> of 15-65 characters that names the page, not just the brand.",
      "fix": "Rewrite the <title> of {{url}} to 15-65 characters that names the specific page, brand last. Evidence: {{evidence}}.",
      "result": "pass",
      "evidence": "title 55 chars: \"BestHumanizer — make AI text read like a human wrote it\"",
      "points": 1
    },
    {
      "id": "meta-description",
      "domain": "structure",
      "weight": 3,
      "tier": "essential",
      "title": "Meta description is present and useful",
      "how": "Meta description 50-170 characters, unique per page.",
      "fix": "Write a 50-170 character meta description for {{url}} that states what the page answers. Evidence: {{evidence}}.",
      "result": "pass",
      "evidence": "162 chars",
      "points": 1
    },
    {
      "id": "one-h1",
      "domain": "structure",
      "weight": 3,
      "tier": "essential",
      "title": "Exactly one H1 states the page subject",
      "how": "One H1 in the document, not empty, distinct from the title tag.",
      "fix": "Ensure exactly one non-empty H1 on {{url}} that states the page subject; demote extra H1s to H2. Evidence: {{evidence}}.",
      "result": "fail",
      "evidence": "0 H1: \"\"",
      "points": 0
    },
    {
      "id": "heading-order",
      "domain": "structure",
      "weight": 2,
      "tier": "recommended",
      "title": "Headings are sequential and descriptive",
      "how": "No skipped levels; H2s describe the section, not \"Section 1\".",
      "fix": "Fix the heading outline on {{url}}: no skipped levels (H2 to H4), and make each H2 describe its section. Evidence: {{evidence}}.",
      "result": "partial",
      "evidence": "0 skipped level(s), 0 H2(s)",
      "points": 0.5
    },
    {
      "id": "canonical",
      "domain": "structure",
      "weight": 3,
      "tier": "essential",
      "title": "Canonical points at itself",
      "how": "A rel=canonical link to the URL being served (or the deliberate canonical target).",
      "fix": "Add a self-referencing rel=canonical to {{url}} that matches the URL actually served (https, final host, no tracking params). Evidence: {{evidence}}.",
      "result": "fail",
      "evidence": "no canonical link",
      "points": 0
    },
    {
      "id": "lang",
      "domain": "structure",
      "weight": 2,
      "tier": "essential",
      "title": "Language is declared and well-formed",
      "how": "html lang is a valid BCP-47 tag (e.g. en, en-US).",
      "fix": "Set a valid BCP-47 lang attribute on <html> for {{origin}} (for example lang=\"en-US\"). Evidence: {{evidence}}.",
      "result": "pass",
      "evidence": "lang=\"en\"",
      "points": 1
    },
    {
      "id": "og-tags",
      "domain": "structure",
      "weight": 2,
      "tier": "recommended",
      "title": "Open Graph title, description and image",
      "how": "og:title, og:description and og:image (1200x630) present.",
      "fix": "Add Open Graph tags to {{url}}: og:title, og:description, og:image (1200x630 absolute URL), og:url, og:type. Evidence: {{evidence}}.",
      "result": "pass",
      "evidence": "og:title yes, og:description yes, og:image yes",
      "points": 1
    },
    {
      "id": "twitter-card",
      "domain": "structure",
      "weight": 1,
      "tier": "recommended",
      "title": "Card metadata for chat previews",
      "how": "twitter:card plus title/description/image.",
      "fix": "Add twitter:card, twitter:title, twitter:description and twitter:image to {{url}}. Evidence: {{evidence}}.",
      "result": "pass",
      "evidence": "twitter:card present",
      "points": 1
    },
    {
      "id": "viewport",
      "domain": "structure",
      "weight": 1,
      "tier": "optional",
      "title": "Viewport is set and pinch-zoom is not disabled",
      "how": "A viewport meta without user-scalable=no or maximum-scale=1.",
      "fix": "Set <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"> on {{origin}} and remove user-scalable=no / maximum-scale=1. Evidence: {{evidence}}.",
      "result": "pass",
      "evidence": "viewport \"width=device-width, initial-scale=1.0\"",
      "points": 1
    },
    {
      "id": "landmarks",
      "domain": "structure",
      "weight": 2,
      "tier": "recommended",
      "title": "Content is separated by landmarks",
      "how": "main or article element, plus nav/header/footer.",
      "fix": "Wrap the primary copy of {{url}} in <main> or <article> and keep nav/header/footer outside it, so a parser can isolate the content. Evidence: {{evidence}}.",
      "result": "partial",
      "evidence": "no main/article element",
      "points": 0.5
    },
    {
      "id": "a11y-names",
      "domain": "structure",
      "weight": 3,
      "tier": "essential",
      "title": "Controls expose accessible names",
      "how": "Every button, link and input has text, aria-label or an associated label.",
      "fix": "Give every interactive control on {{url}} an accessible name: visible text, aria-label, or a <label for>. Evidence: {{evidence}}.",
      "result": "pass",
      "evidence": "0 control(s) without an accessible name",
      "points": 1
    },
    {
      "id": "link-text",
      "domain": "structure",
      "weight": 1,
      "tier": "recommended",
      "title": "Link text describes the destination",
      "how": "No \"click here\" / \"read more\" as the only link text on sampled pages.",
      "fix": "Replace generic link text on {{url}} (\"click here\", \"read more\") with the destination name. Evidence: {{evidence}}.",
      "result": "pass",
      "evidence": "0 generic link label(s)",
      "points": 1
    },
    {
      "id": "href-lang",
      "domain": "structure",
      "weight": 1,
      "tier": "optional",
      "title": "Alternate language versions are declared",
      "how": "hreflang plus x-default when more than one locale is served.",
      "fix": "Declare hreflang alternates plus x-default on {{origin}} for each locale you serve. Evidence: {{evidence}}.",
      "result": "not_applicable",
      "evidence": "single locale site",
      "points": null
    },
    {
      "id": "img-alt",
      "domain": "structure",
      "weight": 2,
      "tier": "recommended",
      "title": "Images carry text alternatives",
      "how": "All content images have alt, decorative images have alt=\"\".",
      "fix": "Add alt text to content images on {{url}} and alt=\"\" to decorative ones. Evidence: {{evidence}}.",
      "result": "not_applicable",
      "evidence": "0/0 images without alt",
      "points": null
    },
    {
      "id": "jsonld",
      "domain": "data",
      "weight": 4,
      "tier": "essential",
      "title": "JSON-LD structured data is present",
      "how": "At least one application/ld+json block with a parseable @context.",
      "fix": "Add a JSON-LD block to {{url}} with @context https://schema.org and a @type that matches the page. Evidence: {{evidence}}.",
      "result": "fail",
      "evidence": "no JSON-LD",
      "points": 0
    },
    {
      "id": "jsonld-type",
      "domain": "data",
      "weight": 3,
      "tier": "recommended",
      "title": "Declared type matches the page",
      "how": "WebPage/Organization for a homepage; Article, Product, Service, FAQPage, HowTo as appropriate.",
      "fix": "Declare the correct schema type on {{url}} ({{evidence}}) — Product for a product page, Article for editorial, Service for a service, FAQPage when the page answers questions.",
      "result": "fail",
      "evidence": "no declared type",
      "points": 0
    },
    {
      "id": "org-entity",
      "domain": "data",
      "weight": 3,
      "tier": "recommended",
      "title": "Publisher entity is declared and linked",
      "how": "Organization or Person node with name, url, logo, and sameAs to the profiles the brand owns.",
      "fix": "Add an Organization (or Person) JSON-LD node on {{origin}} with name, url, logo, and sameAs links to the profiles the brand actually owns. Evidence: {{evidence}}.",
      "result": "fail",
      "evidence": "no Organization/Person node",
      "points": 0
    },
    {
      "id": "dates",
      "domain": "data",
      "weight": 2,
      "tier": "recommended",
      "title": "Published and modified dates are declared",
      "how": "datePublished and dateModified in the page schema.",
      "fix": "Add datePublished and dateModified to the schema on {{url}}, kept in sync with the visible date. Evidence: {{evidence}}.",
      "result": "partial",
      "evidence": "no dates in schema",
      "points": 0.5
    },
    {
      "id": "breadcrumb",
      "domain": "data",
      "weight": 1,
      "tier": "recommended",
      "title": "BreadcrumbList declares the page position",
      "how": "A BreadcrumbList with name+item for each level.",
      "fix": "Add a BreadcrumbList to {{url}} whose items match the visible breadcrumb. Evidence: {{evidence}}.",
      "result": "fail",
      "evidence": "no BreadcrumbList",
      "points": 0
    },
    {
      "id": "product-facts",
      "domain": "data",
      "weight": 4,
      "tier": "recommended",
      "title": "Product facts are in machine-readable form",
      "how": "Product schema with name, image, offers.price, priceCurrency and availability.",
      "fix": "Add Product JSON-LD to {{url}} with name, image, sku, offers.price, offers.priceCurrency, offers.availability and brand. Evidence: {{evidence}}.",
      "result": "not_applicable",
      "evidence": "no Product node (not a product page)",
      "points": null
    },
    {
      "id": "product-variants",
      "domain": "data",
      "weight": 2,
      "tier": "recommended",
      "title": "Variants are modelled",
      "how": "hasVariant / ProductGroup entries or an ItemList of variants with size/colour identifiers.",
      "fix": "Model the variants of {{url}} in schema with hasVariant (or a ProductGroup with variesBy) including each size/colour and its own offer. Evidence: {{evidence}}.",
      "result": "not_applicable",
      "evidence": "n/a",
      "points": null
    },
    {
      "id": "offer-terms",
      "domain": "data",
      "weight": 2,
      "tier": "recommended",
      "title": "Shipping and return terms are in structured data",
      "how": "shippingDetails and hasMerchantReturnPolicy on the offer.",
      "fix": "Add shippingDetails (OfferShippingDetails) and hasMerchantReturnPolicy to the offers on {{url}}. Evidence: {{evidence}}.",
      "result": "not_applicable",
      "evidence": "n/a",
      "points": null
    },
    {
      "id": "faq-howto",
      "domain": "data",
      "weight": 1,
      "tier": "optional",
      "title": "Answer-shaped extras where the page answers questions",
      "how": "FAQPage / HowTo / Question blocks when the page contains those answers.",
      "fix": "Mark the question-and-answer pairs on {{url}} as FAQPage, and any ordered steps as HowTo, matching the visible copy. Evidence: {{evidence}}.",
      "result": "not_applicable",
      "evidence": "",
      "points": null
    },
    {
      "id": "jsonld-truthful",
      "domain": "data",
      "weight": 2,
      "tier": "recommended",
      "title": "Structured data matches visible content",
      "how": "No price/name mismatch between JSON-LD and the rendered HTML.",
      "fix": "Reconcile JSON-LD with the visible page on {{url}}: {{evidence}} — the declared values must equal what a human sees.",
      "result": "pass",
      "evidence": "no price to compare",
      "points": 1
    },
    {
      "id": "reviews",
      "domain": "data",
      "weight": 1,
      "tier": "optional",
      "title": "Ratings or reviews are declared, if real",
      "how": "AggregateRating / Review present only where genuinely shown.",
      "fix": "If reviews are genuinely shown on {{url}}, declare AggregateRating with reviewCount and the individual Review nodes; otherwise leave them out. Evidence: {{evidence}}.",
      "result": "not_applicable",
      "evidence": "ratings",
      "points": null
    },
    {
      "id": "jsonld-valid",
      "domain": "data",
      "weight": 4,
      "tier": "essential",
      "title": "Structured data validates",
      "how": "No parse errors, no missing required properties for the declared type.",
      "fix": "Fix the JSON-LD validation errors on {{url}}: {{evidence}}. Every block must parse and carry the required properties for its type.",
      "result": "fail",
      "evidence": "0 block(s) parse cleanly",
      "points": 0
    },
    {
      "id": "sitemap",
      "domain": "discovery",
      "weight": 4,
      "tier": "essential",
      "title": "A sitemap is declared and lists live URLs",
      "how": "Sitemap referenced in robots.txt, returns XML, entries return 200.",
      "fix": "Publish a sitemap index on {{origin}}, reference it from robots.txt, and list only 200-status canonical URLs. Evidence: {{evidence}}.",
      "result": "pass",
      "evidence": "declared in robots, 2 URLs in first sitemap",
      "points": 1
    },
    {
      "id": "sitemap-lastmod",
      "domain": "discovery",
      "weight": 2,
      "tier": "recommended",
      "title": "Sitemap entries carry real lastmod",
      "how": "lastmod present and not identical for every URL unless that is true.",
      "fix": "Add truthful <lastmod> values to the sitemap on {{origin}} from real content timestamps. Evidence: {{evidence}}.",
      "result": "partial",
      "evidence": "no lastmod",
      "points": 0.5
    },
    {
      "id": "sitemap-segmented",
      "domain": "discovery",
      "weight": 1,
      "tier": "recommended",
      "title": "Sitemaps are split by content type",
      "how": "A sitemap index with child sitemaps for pages, products, posts.",
      "fix": "Split the sitemap on {{origin}} into an index plus child sitemaps per content type (pages, products, posts, images). Evidence: {{evidence}}.",
      "result": "pass",
      "evidence": "index of child sitemaps",
      "points": 1
    },
    {
      "id": "llms-txt",
      "domain": "discovery",
      "weight": 3,
      "tier": "essential",
      "title": "/llms.txt exists and is served as text",
      "how": "200 at /llms.txt (or /.well-known/llms.txt) with text/plain and a non-empty body.",
      "fix": "Publish /llms.txt on {{origin}} as text/plain: a single H1, a one-line blockquote summary, then H2 sections of [name](url) links to the pages that answer the most common questions. Evidence: {{evidence}}.",
      "result": "partial",
      "evidence": "HTTP 200, 1967 bytes, text/html",
      "points": 0.5
    },
    {
      "id": "llms-conform",
      "domain": "discovery",
      "weight": 2,
      "tier": "recommended",
      "title": "llms.txt follows the published format",
      "how": "H1 first, blockquote summary, H2 sections, links in [name](url) form, optional ## Optional section.",
      "fix": "Restructure /llms.txt on {{origin}} to the published format: H1, blockquote summary under it, H2 sections whose bullets are [name](url) with optional \": note\" suffixes, and a ## Optional section for secondary links. Evidence: {{evidence}}.",
      "result": "fail",
      "evidence": "H1 false, blockquote false, 0 H2, 0 links",
      "points": 0
    },
    {
      "id": "llms-full",
      "domain": "discovery",
      "weight": 1,
      "tier": "optional",
      "title": "/llms-full.txt offers one-request ingestion",
      "how": "A single-document variant exists.",
      "fix": "Publish /llms-full.txt on {{origin}} concatenating the primary content of the site in Markdown for one-request ingestion, and link it from /llms.txt. Evidence: {{evidence}}.",
      "result": "pass",
      "evidence": "llms-full.txt HTTP 200",
      "points": 1
    },
    {
      "id": "md-mirror",
      "domain": "discovery",
      "weight": 2,
      "tier": "recommended",
      "title": "A Markdown mirror of the page exists",
      "how": "/<path>.md or /<path>/index.md returns text/markdown.",
      "fix": "Publish a Markdown mirror for each page on {{origin}}: serve /<path>.md with Content-Type text/markdown and the same content as the HTML. Evidence: {{evidence}}.",
      "result": "partial",
      "evidence": "https://besthumanizer.app.md → HTTP 200",
      "points": 0.5
    },
    {
      "id": "md-negotiation",
      "domain": "discovery",
      "weight": 3,
      "tier": "recommended",
      "title": "Content negotiation returns Markdown on request",
      "how": "Accept: text/markdown yields text/markdown, with Vary: Accept.",
      "fix": "Support Accept: text/markdown on {{origin}}: return the Markdown mirror with Content-Type text/markdown and add Vary: Accept so caches stay separate. Evidence: {{evidence}}.",
      "result": "fail",
      "evidence": "Accept: text/markdown → text/html",
      "points": 0
    },
    {
      "id": "md-alternate",
      "domain": "discovery",
      "weight": 1,
      "tier": "recommended",
      "title": "HTML advertises its Markdown twin",
      "how": "<link rel=\"alternate\" type=\"text/markdown\" href=\"...\">.",
      "fix": "Add <link rel=\"alternate\" type=\"text/markdown\" href=\"/<path>.md\"> to the head of {{url}}. Evidence: {{evidence}}.",
      "result": "fail",
      "evidence": "no <link rel=alternate type=text/markdown>",
      "points": 0
    },
    {
      "id": "sitemap-md",
      "domain": "discovery",
      "weight": 1,
      "tier": "recommended",
      "title": "A Markdown sitemap lists the pages in one read",
      "how": "/sitemap.md returns text/markdown listing the site's pages as [title](url).",
      "fix": "Publish /sitemap.md on {{origin}}: a Markdown list of the site's pages as [title](url), one line each, so an agent gets the whole map in one cheap request. Evidence: {{evidence}}.",
      "result": "partial",
      "evidence": "sitemap.md lists 0 link(s)",
      "points": 0.5
    },
    {
      "id": "link-headers",
      "domain": "discovery",
      "weight": 2,
      "tier": "recommended",
      "title": "HTTP Link headers point at machine resources",
      "how": "Link: <...>; rel=\"describedby\"/\"alternate\"/\"api-catalog\" on page responses.",
      "fix": "Add Link response headers on {{origin}} pointing at its machine-readable resources: rel=\"describedby\" to /llms.txt, rel=\"api-catalog\" to /.well-known/api-catalog, rel=\"alternate\" to the Markdown mirror. Evidence: {{evidence}}.",
      "result": "fail",
      "evidence": "no Link header",
      "points": 0
    },
    {
      "id": "dns-aid",
      "domain": "discovery",
      "weight": 1,
      "tier": "bonus",
      "title": "DNS advertises agent endpoints",
      "how": "A discovery TXT/HTTPS record (e.g. _agents or _index._agents) resolves.",
      "fix": "Publish a DNS discovery record for {{origin}} (a TXT record listing the agent endpoints the site exposes) so an agent learns the surface before fetching a page. Evidence: {{evidence}}.",
      "result": "fail",
      "evidence": "no _agents TXT record",
      "points": 0
    },
    {
      "id": "agents-md",
      "domain": "discovery",
      "weight": 2,
      "tier": "recommended",
      "title": "A skill file tells coding agents how to work with the site",
      "how": "/AGENTS.md or /.well-known/agents.md exists.",
      "fix": "Publish /AGENTS.md on {{origin}} describing how a coding agent should build against or use the site: install, configuration, and worked examples. Evidence: {{evidence}}.",
      "result": "pass",
      "evidence": "AGENTS.md HTTP 200",
      "points": 1
    },
    {
      "id": "agents-md-depth",
      "domain": "discovery",
      "weight": 1,
      "tier": "recommended",
      "title": "The skill file is substantive",
      "how": "Contains at least two of: install, configuration, usage examples.",
      "fix": "Deepen /AGENTS.md on {{origin}} to include install steps, configuration, and at least one complete usage example. Evidence: {{evidence}}.",
      "result": "partial",
      "evidence": "0/3 of install, configuration, examples",
      "points": 0.5
    },
    {
      "id": "content-signal",
      "domain": "discovery",
      "weight": 2,
      "tier": "recommended",
      "title": "Content usage preferences are declared",
      "how": "A Content-Signal line stating search / ai-input / ai-train.",
      "fix": "Add a Content-Signal line to robots.txt on {{origin}}, e.g. \"Content-Signal: search=yes, ai-input=yes, ai-train=no\", so usage terms are machine-readable. Evidence: {{evidence}}.",
      "result": "fail",
      "evidence": "no Content-Signal line",
      "points": 0
    },
    {
      "id": "llms-indexes-agents",
      "domain": "discovery",
      "weight": 1,
      "tier": "recommended",
      "title": "The index file advertises the agent entry points",
      "how": "llms.txt or the discovery index names the MCP/API endpoints.",
      "fix": "Add a section to /llms.txt on {{origin}} listing the machine interfaces an agent can call (API base, MCP endpoint, A2A card) with one line each. Evidence: {{evidence}}.",
      "result": "pass",
      "evidence": "llms.txt names machine interfaces",
      "points": 1
    },
    {
      "id": "describedby",
      "domain": "discovery",
      "weight": 1,
      "tier": "optional",
      "title": "The page declares the index that covers it",
      "how": "rel=\"describedby\" link element or Link header to the covering llms.txt.",
      "fix": "Add a rel=\"describedby\" link to the covering /llms.txt from every page on {{origin}} so an agent can widen from one page to the index. Evidence: {{evidence}}.",
      "result": "fail",
      "evidence": "rel=describedby to the index",
      "points": 0
    },
    {
      "id": "discovery-file-404",
      "domain": "discovery",
      "weight": 1,
      "tier": "optional",
      "title": "Discovery files do not 404 quietly",
      "how": "Every file the site advertises resolves; no advertised card points at a dead endpoint.",
      "fix": "Remove or publish the advertised-but-missing discovery artefacts on {{origin}}: {{evidence}}. A card for an interface that does not exist is worse than no card.",
      "result": "pass",
      "evidence": "0 advertised file(s) missing",
      "points": 1
    },
    {
      "id": "openapi",
      "domain": "interfaces",
      "weight": 4,
      "tier": "essential",
      "title": "An OpenAPI description is published",
      "how": "/openapi.json or /.well-known/openapi.json parses and declares paths.",
      "fix": "Publish /openapi.json on {{origin}} (OpenAPI 3.1) describing every public endpoint, with operation summaries and example responses. Evidence: {{evidence}}.",
      "result": "partial",
      "evidence": "openapi.json HTTP 200",
      "points": 0.5
    },
    {
      "id": "api-catalog",
      "domain": "interfaces",
      "weight": 2,
      "tier": "recommended",
      "title": "An API catalog linkset is published",
      "how": "/.well-known/api-catalog returns an RFC 9727 linkset.",
      "fix": "Publish /.well-known/api-catalog on {{origin}} as an RFC 9727 linkset pointing at the API, its OpenAPI description and its docs. Evidence: {{evidence}}.",
      "result": "fail",
      "evidence": "no linkset",
      "points": 0
    },
    {
      "id": "oauth-protected-resource",
      "domain": "interfaces",
      "weight": 2,
      "tier": "recommended",
      "title": "Protected resources declare their authorization server",
      "how": "/.well-known/oauth-protected-resource (RFC 9728) resolves.",
      "fix": "Publish /.well-known/oauth-protected-resource on {{origin}} naming the resource and its authorization server, so an agent can discover how to get a token without scraping docs. Evidence: {{evidence}}.",
      "result": "pass",
      "evidence": "metadata present",
      "points": 1
    },
    {
      "id": "oauth-as",
      "domain": "interfaces",
      "weight": 2,
      "tier": "recommended",
      "title": "Authorization server metadata is published",
      "how": "/.well-known/oauth-authorization-server (RFC 8414) resolves.",
      "fix": "Publish /.well-known/oauth-authorization-server on {{origin}} with issuer, authorization_endpoint, token_endpoint and grant types. Evidence: {{evidence}}.",
      "result": "pass",
      "evidence": "token endpoint declared",
      "points": 1
    },
    {
      "id": "scopes",
      "domain": "interfaces",
      "weight": 2,
      "tier": "recommended",
      "title": "Permissions are scoped",
      "how": "OAuth/OpenAPI declares named scopes or per-endpoint permissions rather than all-or-nothing.",
      "fix": "Replace all-or-nothing credentials on {{origin}} with named scopes (for example read:catalog, write:cart) declared in the OpenAPI security scheme and the authorization-server metadata. Evidence: {{evidence}}.",
      "result": "pass",
      "evidence": "scopes declared",
      "points": 1
    },
    {
      "id": "auth-md",
      "domain": "interfaces",
      "weight": 2,
      "tier": "recommended",
      "title": "Agent sign-up is documented for machines",
      "how": "/auth.md or a machine-readable credential section exists.",
      "fix": "Publish /auth.md on {{origin}} telling an agent how to register, get a credential and revoke it, with the exact request/response shapes. Evidence: {{evidence}}.",
      "result": "pass",
      "evidence": "auth.md HTTP 200",
      "points": 1
    },
    {
      "id": "mcp-card",
      "domain": "interfaces",
      "weight": 3,
      "tier": "essential",
      "title": "An MCP server card describes the server",
      "how": "/.well-known/mcp.json or /.well-known/mcp/server-card.json parses.",
      "fix": "Publish an MCP server card at /.well-known/mcp.json on {{origin}}: name, description, transport, endpoint, and the tool list with input schemas. Evidence: {{evidence}}.",
      "result": "pass",
      "evidence": "MCP card: error",
      "points": 1
    },
    {
      "id": "mcp-card-fields",
      "domain": "interfaces",
      "weight": 2,
      "tier": "recommended",
      "title": "The MCP card carries the fields a client needs",
      "how": "Transport, endpoint, auth requirement and at least one tool with an input schema.",
      "fix": "Complete the MCP server card on {{origin}}: transport, endpoint URL, auth requirement, and every tool with a declared input schema and description. Evidence: {{evidence}}.",
      "result": "fail",
      "evidence": "0/4 required groups in the card",
      "points": 0
    },
    {
      "id": "mcp-live",
      "domain": "interfaces",
      "weight": 3,
      "tier": "essential",
      "title": "The advertised MCP endpoint answers",
      "how": "The endpoint from the card responds to an initialize/tools list request.",
      "fix": "Make the MCP endpoint advertised by {{origin}} actually answer: respond to initialize and tools/list at the URL in the card, and keep the card in sync with the deployed tool set. Evidence: {{evidence}}.",
      "result": "not_applicable",
      "evidence": "MCP endpoint answered no endpoint in card",
      "points": null
    },
    {
      "id": "a2a-card",
      "domain": "interfaces",
      "weight": 2,
      "tier": "recommended",
      "title": "An A2A agent card is published",
      "how": "/.well-known/agent-card.json parses with name, capabilities, skills.",
      "fix": "Publish a /.well-known/agent-card.json on {{origin}} describing the agent: name, description, capabilities, skills and endpoint. Evidence: {{evidence}}.",
      "result": "pass",
      "evidence": "agent card present",
      "points": 1
    },
    {
      "id": "agent-skills",
      "domain": "interfaces",
      "weight": 1,
      "tier": "bonus",
      "title": "Agent skills are indexable",
      "how": "/.well-known/agent-skills/index.json lists skill documents.",
      "fix": "Publish /.well-known/agent-skills/index.json on {{origin}} listing each skill document with name, description and URL. Evidence: {{evidence}}.",
      "result": "pass",
      "evidence": "skills index present",
      "points": 1
    },
    {
      "id": "webmcp",
      "domain": "interfaces",
      "weight": 1,
      "tier": "bonus",
      "title": "Site actions are exposed to in-browser agents",
      "how": "WebMCP tool registration is present in the page source.",
      "fix": "Register the site's real actions (search, filter, add to cart) as WebMCP tools on {{origin}} so a browser agent can call them without guessing at the DOM. Evidence: {{evidence}}.",
      "result": "fail",
      "evidence": "no WebMCP registration",
      "points": 0
    },
    {
      "id": "ard",
      "domain": "interfaces",
      "weight": 1,
      "tier": "bonus",
      "title": "One index lists every agent interface",
      "how": "An ARD / AI-catalog manifest enumerates the MCP, A2A, skills and API surfaces.",
      "fix": "Publish an ARD manifest at /.well-known/ard.json on {{origin}} listing every agentic artefact with its type and URL, and advertise it with rel=\"ard\". Evidence: {{evidence}}.",
      "result": "pass",
      "evidence": "ARD manifest present",
      "points": 1
    },
    {
      "id": "agents-json",
      "domain": "interfaces",
      "weight": 1,
      "tier": "recommended",
      "title": "A wildcard agent manifest is published",
      "how": "/.well-known/agents.json parses and enumerates agents or actions with a URL each.",
      "fix": "Publish /.well-known/agents.json on {{origin}}: a JSON document naming each agent or action the site accepts, with the URL to call and the input it expects. Evidence: {{evidence}}.",
      "result": "partial",
      "evidence": "0 agent(s) enumerated",
      "points": 0.5
    },
    {
      "id": "agent-permissions",
      "domain": "interfaces",
      "weight": 1,
      "tier": "recommended",
      "title": "Per-action permissions are declared",
      "how": "/.well-known/agent-permissions.json parses and maps actions to scopes or consent rules.",
      "fix": "Publish /.well-known/agent-permissions.json on {{origin}} mapping each action an agent may take to the scope it needs and whether a human must confirm it. Evidence: {{evidence}}.",
      "result": "partial",
      "evidence": "permissions file has no scope/consent keys",
      "points": 0.5
    },
    {
      "id": "nlweb-ask",
      "domain": "interfaces",
      "weight": 1,
      "tier": "recommended",
      "title": "A natural-language query endpoint answers",
      "how": "GET /ask?query= returns a JSON answer with results, not an HTML page.",
      "fix": "Expose a natural-language query endpoint at {{origin}}/ask that accepts ?query= and returns JSON (answer plus result URLs), so an agent can ask the site directly instead of crawling it. Evidence: {{evidence}}.",
      "result": "fail",
      "evidence": "/ask → HTTP 200, text/html",
      "points": 0
    },
    {
      "id": "a2ui",
      "domain": "interfaces",
      "weight": 1,
      "tier": "bonus",
      "title": "The interface is declared for agent-driven UI",
      "how": "The page or its headers declare an A2UI surface, or the app publishes an A2UI manifest.",
      "fix": "Declare an agent-driven UI surface on {{origin}}: mark the page (or publish a manifest) as A2UI so a reviewing agent knows the controls are renderable from a declared schema rather than inferred from the DOM. Evidence: {{evidence}}.",
      "result": "fail",
      "evidence": "no A2UI declaration",
      "points": 0
    },
    {
      "id": "pap",
      "domain": "interfaces",
      "weight": 1,
      "tier": "bonus",
      "title": "A personal-agent profile is published",
      "how": "/.well-known/personal-agent.json parses with the identity and consent rules for agents acting for a signed-in user.",
      "fix": "Publish /.well-known/personal-agent.json on {{origin}} describing how an agent acting for a signed-in person is identified and what it may do on their behalf. Evidence: {{evidence}}.",
      "result": "pass",
      "evidence": "personal-agent profile present",
      "points": 1
    },
    {
      "id": "web-bot-auth",
      "domain": "access",
      "weight": 1,
      "tier": "bonus",
      "title": "The site publishes its own bot signing keys",
      "how": "/.well-known/http-message-signatures-directory returns a JWKS an agent can verify this site's outbound bot requests against.",
      "fix": "Publish a signing-key directory at {{origin}}/.well-known/http-message-signatures-directory so another site can verify requests this site's bots send. Evidence: {{evidence}}.",
      "result": "partial",
      "evidence": "0 signing key(s)",
      "points": 0.5
    },
    {
      "id": "json-errors",
      "domain": "interfaces",
      "weight": 2,
      "tier": "recommended",
      "title": "Errors are machine-readable",
      "how": "Unknown API routes return JSON with a type/title/status, not an HTML error page.",
      "fix": "Return RFC 9457 problem-details JSON for every error on {{origin}}, including unknown routes, so an agent can recover instead of parsing an HTML error page. Evidence: {{evidence}}.",
      "result": "pass",
      "evidence": "JSON problem response",
      "points": 1
    },
    {
      "id": "rate-limit-headers",
      "domain": "interfaces",
      "weight": 1,
      "tier": "recommended",
      "title": "Rate limits are declared in headers",
      "how": "RateLimit-* / X-RateLimit-* or Retry-After on API responses.",
      "fix": "Emit RateLimit-Limit / RateLimit-Remaining / RateLimit-Reset (and Retry-After on 429) from the API on {{origin}}. Evidence: {{evidence}}.",
      "result": "fail",
      "evidence": "no rate-limit headers",
      "points": 0
    },
    {
      "id": "fc-friendly",
      "domain": "interfaces",
      "weight": 2,
      "tier": "recommended",
      "title": "The API is easy to call from a model",
      "how": "Flat parameters, enums over free text, descriptions on every field, no nested auth dance.",
      "fix": "Simplify the API surface on {{origin}} for function calling: flat named parameters, enums instead of free-text where possible, and a description on every field in the OpenAPI spec. Evidence: {{evidence}}.",
      "result": "fail",
      "evidence": "0 described properties in the schema",
      "points": 0
    },
    {
      "id": "dev-portal",
      "domain": "interfaces",
      "weight": 2,
      "tier": "recommended",
      "title": "A developer portal is reachable from the homepage",
      "how": "A link to docs/API reference from the homepage or main nav.",
      "fix": "Link the developer docs from the homepage and main navigation of {{origin}} — one hop, not three. Evidence: {{evidence}}.",
      "result": "fail",
      "evidence": "no developer link",
      "points": 0
    },
    {
      "id": "public-api",
      "domain": "interfaces",
      "weight": 3,
      "tier": "recommended",
      "title": "A public API answers without a human",
      "how": "At least one documented endpoint responds to an unauthenticated request.",
      "fix": "Expose at least one unauthenticated read endpoint on {{origin}} that returns the site's core facts as JSON, and document it in the OpenAPI spec. Evidence: {{evidence}}.",
      "result": "partial",
      "evidence": "no API declared",
      "points": 0.5
    },
    {
      "id": "agent-instruction",
      "domain": "interfaces",
      "weight": 2,
      "tier": "recommended",
      "title": "The site tells an agent when to use it",
      "how": "A when-to-use / instructions block in the manifest, llms.txt or MCP card.",
      "fix": "Add a when-to-use block to {{origin}} (in llms.txt, the MCP card or the agent manifest) stating what the agent is for, what it must not do, and one worked call. Evidence: {{evidence}}.",
      "result": "fail",
      "evidence": "no when-to-use block",
      "points": 0
    },
    {
      "id": "idempotency",
      "domain": "interfaces",
      "weight": 1,
      "tier": "optional",
      "title": "Writes are retry-safe",
      "how": "Idempotency-Key accepted on POST, or a documented natural key.",
      "fix": "Accept an Idempotency-Key header on mutating endpoints of {{origin}} and document it, so an agent can retry without duplicating work. Evidence: {{evidence}}.",
      "result": "fail",
      "evidence": "Idempotency-Key documented",
      "points": 0
    },
    {
      "id": "agent-discovery-path",
      "domain": "interfaces",
      "weight": 1,
      "tier": "optional",
      "title": "Discovery lives at predictable paths",
      "how": "The well-known paths used are the conventional ones, not bespoke URLs.",
      "fix": "Move the machine-readable artefacts on {{origin}} to their conventional paths (/.well-known/*, /llms.txt, /sitemap.xml) and keep redirects from the old ones. Evidence: {{evidence}}.",
      "result": "pass",
      "evidence": "conventional paths probed",
      "points": 1
    },
    {
      "id": "payment-negotiation",
      "domain": "interfaces",
      "weight": 1,
      "tier": "optional",
      "title": "Paid access is discoverable by an agent",
      "how": "A 402 challenge carries the payment requirements, or pricing is machine-readable.",
      "fix": "Make paid access discoverable on {{origin}}: return HTTP 402 with a machine-readable list of accepted payment options, or publish a machine-readable price list. Evidence: {{evidence}}.",
      "result": "fail",
      "evidence": "x402 discovery HTTP 404",
      "points": 0
    },
    {
      "id": "ucp-profile",
      "domain": "commerce",
      "weight": 3,
      "tier": "recommended",
      "title": "A commerce profile is published",
      "how": "/.well-known/ucp returns a valid commerce protocol profile.",
      "fix": "Publish a commerce profile at /.well-known/ucp on {{origin}} declaring the merchant, the catalogue, and the endpoints an agent may call. Evidence: {{evidence}}.",
      "result": "not_applicable",
      "evidence": "commerce profile present",
      "points": null
    },
    {
      "id": "agent-endpoint",
      "domain": "commerce",
      "weight": 3,
      "tier": "recommended",
      "title": "The advertised commerce endpoint is live",
      "how": "The endpoint in the profile responds with catalogue/cart operations.",
      "fix": "Make the commerce endpoint advertised by {{origin}} answer for real: catalogue read, cart create, cart update. Register {{evidence}}.",
      "result": "not_applicable",
      "evidence": "no endpoint advertised",
      "points": null
    },
    {
      "id": "checkout-permalink",
      "domain": "commerce",
      "weight": 2,
      "tier": "recommended",
      "title": "A cart can be handed back to a shopper",
      "how": "A cart or checkout permalink an agent can send to a human.",
      "fix": "Add a shareable cart/checkout permalink on {{origin}} that carries line items in the URL, so an agent can build a cart and hand the link to a person. Evidence: {{evidence}}.",
      "result": "not_applicable",
      "evidence": "no cart permalink",
      "points": null
    },
    {
      "id": "x402",
      "domain": "commerce",
      "weight": 1,
      "tier": "bonus",
      "title": "Machine payment is accepted",
      "how": "x402 accepts array on a 402 response, or an advertised payment resource.",
      "fix": "Accept machine payment on {{origin}}: answer with HTTP 402 and an x402 accepts array (asset, amount, network, payTo) on the paid resource. Evidence: {{evidence}}.",
      "result": "not_applicable",
      "evidence": "no x402 accepts array",
      "points": null
    },
    {
      "id": "ap2",
      "domain": "commerce",
      "weight": 1,
      "tier": "bonus",
      "title": "Delegated payment authorization is advertised",
      "how": "AP2 support declared on the agent card or commerce profile.",
      "fix": "Advertise delegated payment authorization on {{origin}} (AP2 extension on the agent card or commerce profile) with the mandate shapes you accept. Evidence: {{evidence}}.",
      "result": "not_applicable",
      "evidence": "AP2 advertised",
      "points": null
    },
    {
      "id": "acp-profile",
      "domain": "commerce",
      "weight": 1,
      "tier": "bonus",
      "title": "A checkout protocol profile is published",
      "how": "/.well-known/acp.json or equivalent.",
      "fix": "Publish a checkout-protocol profile at /.well-known/acp.json on {{origin}} naming the checkout endpoint and supported payment methods. Evidence: {{evidence}}.",
      "result": "not_applicable",
      "evidence": "acp profile HTTP 404",
      "points": null
    },
    {
      "id": "add-to-cart",
      "domain": "commerce",
      "weight": 3,
      "tier": "essential",
      "title": "Add-to-cart is a real labelled control",
      "how": "A <button> or <input type=submit> inside a form, with an accessible name, not a div with a click handler.",
      "fix": "Replace the add-to-cart div on {{url}} with a real <button type=\"submit\"> inside a <form>, with an accessible name that includes the product. Evidence: {{evidence}}.",
      "result": "not_applicable",
      "evidence": "no add-to-cart control",
      "points": null
    },
    {
      "id": "variant-controls",
      "domain": "commerce",
      "weight": 3,
      "tier": "essential",
      "title": "Variant pickers are real form controls",
      "how": "select/input/radiogroup elements with names and labels for each option.",
      "fix": "Convert the variant pickers on {{url}} to real form controls (select or radio inputs) with a label per option, so an agent can set a value. Evidence: {{evidence}}.",
      "result": "not_applicable",
      "evidence": "no variant form controls",
      "points": null
    },
    {
      "id": "site-search",
      "domain": "commerce",
      "weight": 3,
      "tier": "essential",
      "title": "Search works through a GET URL",
      "how": "A search form that produces a shareable ?q= URL returning results in HTML.",
      "fix": "Make site search on {{origin}} work over GET with a ?q= parameter that returns results in the server HTML, so an agent can construct a search URL. Evidence: {{evidence}}.",
      "result": "fail",
      "evidence": "no GET search form",
      "points": 0
    },
    {
      "id": "pdp-facts-html",
      "domain": "commerce",
      "weight": 4,
      "tier": "essential",
      "title": "Price, stock and variants are in the HTML",
      "how": "Product name, price, currency and availability in the raw HTML of the product page.",
      "fix": "Put product name, price, currency and availability in the server HTML of {{url}} — not fetched client-side — with the variants listed. Evidence: {{evidence}}.",
      "result": "not_applicable",
      "evidence": "not a commerce page",
      "points": null
    },
    {
      "id": "guest-checkout",
      "domain": "commerce",
      "weight": 2,
      "tier": "recommended",
      "title": "A cart can be built without an account",
      "how": "Cart add succeeds unauthenticated; checkout does not require login before payment.",
      "fix": "Allow an unauthenticated cart on {{origin}} and do not force account creation before payment; if accounts are required by policy, say so in llms.txt with the exact steps. Evidence: {{evidence}}.",
      "result": "not_applicable",
      "evidence": "no login wall before cart",
      "points": null
    },
    {
      "id": "checkout-docs",
      "domain": "commerce",
      "weight": 2,
      "tier": "recommended",
      "title": "Checkout terms an agent needs are documented",
      "how": "Shipping, returns, tax/VAT and lead time stated in text an agent can read.",
      "fix": "Document the checkout terms an agent must know on {{origin}}: shipping cost and regions, return window, tax handling, and lead time. Evidence: {{evidence}}.",
      "result": "not_applicable",
      "evidence": "checkout terms incomplete",
      "points": null
    },
    {
      "id": "policy-links",
      "domain": "commerce",
      "weight": 2,
      "tier": "recommended",
      "title": "Policy pages are linked sitewide",
      "how": "Returns, shipping, terms, privacy and contact reachable from every page.",
      "fix": "Link returns, shipping, terms, privacy and contact from the footer of every page on {{origin}}. Evidence: {{evidence}}.",
      "result": "not_applicable",
      "evidence": "0/5 policy pages linked",
      "points": null
    },
    {
      "id": "author",
      "domain": "trust",
      "weight": 3,
      "tier": "recommended",
      "title": "A named, accountable author or publisher",
      "how": "Visible author byline or declared publisher with a real identity.",
      "fix": "Show a named author or accountable publisher on {{url}}, with a link to an about page that establishes who they are. Evidence: {{evidence}}.",
      "result": "partial",
      "evidence": "no author signal",
      "points": 0.5
    },
    {
      "id": "visible-date",
      "domain": "trust",
      "weight": 2,
      "tier": "recommended",
      "title": "A visible publish or update date",
      "how": "A human-readable date on the page matching the schema dates.",
      "fix": "Show a visible publish or last-updated date on {{url}} that matches dateModified in the schema. Evidence: {{evidence}}.",
      "result": "partial",
      "evidence": "no visible date",
      "points": 0.5
    },
    {
      "id": "sources",
      "domain": "trust",
      "weight": 2,
      "tier": "recommended",
      "title": "Claims are checkable",
      "how": "Outbound links to primary sources for factual claims.",
      "fix": "Add outbound links to primary sources for the factual claims on {{url}} — the operator documentation, study or specification, not another summary. Evidence: {{evidence}}.",
      "result": "fail",
      "evidence": "0 outbound source link(s)",
      "points": 0
    },
    {
      "id": "about-contact",
      "domain": "trust",
      "weight": 2,
      "tier": "recommended",
      "title": "About and contact are reachable",
      "how": "An about page and a contact method one hop from the page.",
      "fix": "Publish a reachable About and Contact page on {{origin}} and link both from the footer. Evidence: {{evidence}}.",
      "result": "fail",
      "evidence": "no about or contact link",
      "points": 0
    },
    {
      "id": "org-completeness",
      "domain": "trust",
      "weight": 2,
      "tier": "recommended",
      "title": "The organization node is complete",
      "how": "legalName, address, contactPoint, logo, sameAs in the Organization schema.",
      "fix": "Complete the Organization schema on {{origin}} with legalName, address, contactPoint, logo and sameAs. Evidence: {{evidence}}.",
      "result": "fail",
      "evidence": "legalName missing",
      "points": 0
    },
    {
      "id": "entity-consistency",
      "domain": "trust",
      "weight": 2,
      "tier": "recommended",
      "title": "The brand is named the same way everywhere",
      "how": "Consistent brand string across title, og:site_name, schema name, llms.txt and footer.",
      "fix": "Make the brand name identical across the title suffix, og:site_name, schema Organization.name, llms.txt heading and footer on {{origin}} — one canonical string. Evidence: {{evidence}}.",
      "result": "pass",
      "evidence": "brand string \"make AI text read like a human\"",
      "points": 1
    },
    {
      "id": "https",
      "domain": "trust",
      "weight": 2,
      "tier": "essential",
      "title": "Every page is served over HTTPS",
      "how": "HTTPS with a valid certificate; http:// redirects to https://.",
      "fix": "Serve {{origin}} only over HTTPS and 301 every http:// URL to https://. Evidence: {{evidence}}.",
      "result": "partial",
      "evidence": "https origin, http:// answered 200",
      "points": 0.5
    },
    {
      "id": "mixed-content",
      "domain": "trust",
      "weight": 1,
      "tier": "recommended",
      "title": "No insecure subresources",
      "how": "No http:// script/img/css on an https page.",
      "fix": "Rewrite the http:// subresources on {{url}} to https:// and keep the scheme relative. Evidence: {{evidence}}.",
      "result": "pass",
      "evidence": "no insecure subresources",
      "points": 1
    },
    {
      "id": "security-headers",
      "domain": "trust",
      "weight": 1,
      "tier": "optional",
      "title": "Baseline security headers are set",
      "how": "HSTS, X-Content-Type-Options: nosniff, and a Referrer-Policy.",
      "fix": "Set Strict-Transport-Security, X-Content-Type-Options: nosniff and Referrer-Policy: strict-origin-when-cross-origin on {{origin}}. Evidence: {{evidence}}.",
      "result": "fail",
      "evidence": "HSTS no, nosniff no",
      "points": 0
    },
    {
      "id": "no-broken-links",
      "domain": "trust",
      "weight": 1,
      "tier": "recommended",
      "title": "Sampled internal links resolve",
      "how": "No 404s among the internal links sampled from the page.",
      "fix": "Fix the broken internal links found on {{url}}: {{evidence}}.",
      "result": "pass",
      "evidence": "0 broken among 0 sampled links",
      "points": 1
    }
  ],
  "crawlers": [
    {
      "ua": "OAI-SearchBot",
      "platform": "ChatGPT search",
      "role": "search",
      "weight": 10,
      "tier": 1,
      "status": 200,
      "bytes": 1967,
      "ms": 22,
      "robotsAllowed": true,
      "rule": null,
      "blocked": false,
      "challenged": false,
      "verdict": "allowed"
    },
    {
      "ua": "ChatGPT-User",
      "platform": "ChatGPT",
      "role": "user",
      "weight": 9,
      "tier": 1,
      "status": 200,
      "bytes": 1967,
      "ms": 21,
      "robotsAllowed": true,
      "rule": null,
      "blocked": false,
      "challenged": false,
      "verdict": "allowed"
    },
    {
      "ua": "Claude-SearchBot",
      "platform": "Claude search",
      "role": "search",
      "weight": 10,
      "tier": 1,
      "status": 200,
      "bytes": 1967,
      "ms": 21,
      "robotsAllowed": true,
      "rule": null,
      "blocked": false,
      "challenged": false,
      "verdict": "allowed"
    },
    {
      "ua": "Claude-User",
      "platform": "Claude",
      "role": "user",
      "weight": 9,
      "tier": 1,
      "status": 200,
      "bytes": 1967,
      "ms": 22,
      "robotsAllowed": true,
      "rule": null,
      "blocked": false,
      "challenged": false,
      "verdict": "allowed"
    },
    {
      "ua": "PerplexityBot",
      "platform": "Perplexity",
      "role": "search",
      "weight": 10,
      "tier": 1,
      "status": 200,
      "bytes": 1967,
      "ms": 21,
      "robotsAllowed": true,
      "rule": null,
      "blocked": false,
      "challenged": false,
      "verdict": "allowed"
    },
    {
      "ua": "Perplexity-User",
      "platform": "Perplexity",
      "role": "user",
      "weight": 9,
      "tier": 1,
      "status": 200,
      "bytes": 1967,
      "ms": 22,
      "robotsAllowed": true,
      "rule": null,
      "blocked": false,
      "challenged": false,
      "verdict": "allowed"
    },
    {
      "ua": "Googlebot",
      "platform": "Google / AI Overviews",
      "role": "search",
      "weight": 10,
      "tier": 1,
      "status": 200,
      "bytes": 1967,
      "ms": 36,
      "robotsAllowed": true,
      "rule": null,
      "blocked": false,
      "challenged": false,
      "verdict": "allowed"
    },
    {
      "ua": "Google-Agent",
      "platform": "Google AI Mode",
      "role": "user",
      "weight": 7,
      "tier": 2,
      "status": 200,
      "bytes": 1967,
      "ms": 48,
      "robotsAllowed": true,
      "rule": null,
      "blocked": false,
      "challenged": false,
      "verdict": "allowed"
    },
    {
      "ua": "Google-GeminiNotebook",
      "platform": "Gemini",
      "role": "user",
      "weight": 5,
      "tier": 2,
      "status": 200,
      "bytes": 1967,
      "ms": 37,
      "robotsAllowed": true,
      "rule": null,
      "blocked": false,
      "challenged": false,
      "verdict": "allowed"
    },
    {
      "ua": "bingbot",
      "platform": "Bing / Copilot",
      "role": "search",
      "weight": 8,
      "tier": 1,
      "status": 200,
      "bytes": 1967,
      "ms": 52,
      "robotsAllowed": true,
      "rule": null,
      "blocked": false,
      "challenged": false,
      "verdict": "allowed"
    },
    {
      "ua": "Applebot",
      "platform": "Siri / Apple Intelligence",
      "role": "search",
      "weight": 6,
      "tier": 2,
      "status": 200,
      "bytes": 1967,
      "ms": 52,
      "robotsAllowed": true,
      "rule": null,
      "blocked": false,
      "challenged": false,
      "verdict": "allowed"
    },
    {
      "ua": "meta-webindexer",
      "platform": "Meta AI",
      "role": "search",
      "weight": 5,
      "tier": 2,
      "status": 200,
      "bytes": 1967,
      "ms": 50,
      "robotsAllowed": true,
      "rule": null,
      "blocked": false,
      "challenged": false,
      "verdict": "allowed"
    },
    {
      "ua": "meta-externalfetcher",
      "platform": "Meta AI",
      "role": "user",
      "weight": 5,
      "tier": 2,
      "status": 200,
      "bytes": 1967,
      "ms": 53,
      "robotsAllowed": true,
      "rule": null,
      "blocked": false,
      "challenged": false,
      "verdict": "allowed"
    },
    {
      "ua": "MistralAI-Index",
      "platform": "Mistral Le Chat",
      "role": "search",
      "weight": 4,
      "tier": 2,
      "status": 200,
      "bytes": 1967,
      "ms": 54,
      "robotsAllowed": true,
      "rule": null,
      "blocked": false,
      "challenged": false,
      "verdict": "allowed"
    },
    {
      "ua": "MistralAI-User",
      "platform": "Mistral Le Chat",
      "role": "user",
      "weight": 4,
      "tier": 2,
      "status": 200,
      "bytes": 1967,
      "ms": 66,
      "robotsAllowed": true,
      "rule": null,
      "blocked": false,
      "challenged": false,
      "verdict": "allowed"
    },
    {
      "ua": "Amzn-SearchBot",
      "platform": "Alexa",
      "role": "search",
      "weight": 4,
      "tier": 2,
      "status": 200,
      "bytes": 1967,
      "ms": 68,
      "robotsAllowed": true,
      "rule": null,
      "blocked": false,
      "challenged": false,
      "verdict": "allowed"
    },
    {
      "ua": "Amzn-User",
      "platform": "Alexa",
      "role": "user",
      "weight": 3,
      "tier": 3,
      "status": 200,
      "bytes": 1967,
      "ms": 76,
      "robotsAllowed": true,
      "rule": null,
      "blocked": false,
      "challenged": false,
      "verdict": "allowed"
    },
    {
      "ua": "DuckAssistBot",
      "platform": "DuckDuckGo AI",
      "role": "search",
      "weight": 3,
      "tier": 3,
      "status": 200,
      "bytes": 1967,
      "ms": 78,
      "robotsAllowed": true,
      "rule": null,
      "blocked": false,
      "challenged": false,
      "verdict": "allowed"
    },
    {
      "ua": "YouBot",
      "platform": "You.com",
      "role": "search",
      "weight": 3,
      "tier": 3,
      "status": 200,
      "bytes": 1967,
      "ms": 78,
      "robotsAllowed": true,
      "rule": null,
      "blocked": false,
      "challenged": false,
      "verdict": "allowed"
    },
    {
      "ua": "GPTBot",
      "platform": "OpenAI training",
      "role": "train",
      "weight": 0,
      "tier": 3,
      "status": 200,
      "bytes": 1967,
      "ms": 77,
      "robotsAllowed": true,
      "rule": null,
      "blocked": false,
      "challenged": false,
      "verdict": "allowed"
    },
    {
      "ua": "ClaudeBot",
      "platform": "Anthropic training",
      "role": "train",
      "weight": 0,
      "tier": 3,
      "status": 200,
      "bytes": 1967,
      "ms": 83,
      "robotsAllowed": true,
      "rule": null,
      "blocked": false,
      "challenged": false,
      "verdict": "allowed"
    },
    {
      "ua": "Google-Extended",
      "platform": "Gemini training",
      "role": "train",
      "weight": 0,
      "tier": 3,
      "status": 200,
      "bytes": 1967,
      "ms": 83,
      "robotsAllowed": true,
      "rule": null,
      "blocked": false,
      "challenged": false,
      "verdict": "allowed"
    },
    {
      "ua": "Applebot-Extended",
      "platform": "Apple training",
      "role": "train",
      "weight": 0,
      "tier": 3,
      "status": 200,
      "bytes": 1967,
      "ms": 88,
      "robotsAllowed": true,
      "rule": null,
      "blocked": false,
      "challenged": false,
      "verdict": "allowed"
    },
    {
      "ua": "CCBot",
      "platform": "Common Crawl",
      "role": "train",
      "weight": 0,
      "tier": 3,
      "status": 200,
      "bytes": 1967,
      "ms": 97,
      "robotsAllowed": true,
      "rule": null,
      "blocked": false,
      "challenged": false,
      "verdict": "allowed"
    },
    {
      "ua": "Bytespider",
      "platform": "ByteDance",
      "role": "train",
      "weight": 0,
      "tier": 4,
      "status": 200,
      "bytes": 1967,
      "ms": 102,
      "robotsAllowed": true,
      "rule": null,
      "blocked": false,
      "challenged": false,
      "verdict": "allowed"
    },
    {
      "ua": "meta-externalagent",
      "platform": "Meta training",
      "role": "train",
      "weight": 0,
      "tier": 3,
      "status": 200,
      "bytes": 1967,
      "ms": 103,
      "robotsAllowed": true,
      "rule": null,
      "blocked": false,
      "challenged": false,
      "verdict": "allowed"
    },
    {
      "ua": "cohere-ai",
      "platform": "Cohere training",
      "role": "train",
      "weight": 0,
      "tier": 4,
      "status": 200,
      "bytes": 1967,
      "ms": 103,
      "robotsAllowed": true,
      "rule": null,
      "blocked": false,
      "challenged": false,
      "verdict": "allowed"
    },
    {
      "ua": "AI2Bot",
      "platform": "Allen Institute",
      "role": "train",
      "weight": 0,
      "tier": 4,
      "status": 200,
      "bytes": 1967,
      "ms": 103,
      "robotsAllowed": true,
      "rule": null,
      "blocked": false,
      "challenged": false,
      "verdict": "allowed"
    },
    {
      "ua": "Diffbot",
      "platform": "Diffbot",
      "role": "other",
      "weight": 1,
      "tier": 4,
      "status": 200,
      "bytes": 1967,
      "ms": 123,
      "robotsAllowed": true,
      "rule": null,
      "blocked": false,
      "challenged": false,
      "verdict": "allowed"
    },
    {
      "ua": "Amazonbot",
      "platform": "Amazon",
      "role": "train",
      "weight": 0,
      "tier": 4,
      "status": 200,
      "bytes": 1967,
      "ms": 126,
      "robotsAllowed": true,
      "rule": null,
      "blocked": false,
      "challenged": false,
      "verdict": "allowed"
    },
    {
      "ua": "GoogleOther",
      "platform": "Google other",
      "role": "other",
      "weight": 1,
      "tier": 4,
      "status": 200,
      "bytes": 1967,
      "ms": 128,
      "robotsAllowed": true,
      "rule": null,
      "blocked": false,
      "challenged": false,
      "verdict": "allowed"
    },
    {
      "ua": "Google-CloudVertexBot",
      "platform": "Vertex AI",
      "role": "train",
      "weight": 0,
      "tier": 4,
      "status": 200,
      "bytes": 1967,
      "ms": 128,
      "robotsAllowed": true,
      "rule": null,
      "blocked": false,
      "challenged": false,
      "verdict": "allowed"
    }
  ],
  "fixes": [
    {
      "id": "ssr-home",
      "domain": "render",
      "title": "Homepage content is in the raw HTML",
      "weight": 5,
      "result": "fail",
      "evidence": "55 chars of text, 0 H1 in raw HTML",
      "recoverable": 5,
      "prompt": "Server-render the homepage of https://besthumanizer.app: ship at least 500 characters of real text and an H1 in the initial HTML response rather than after hydration (SSR/SSG, or prerender). Evidence: 55 chars of text, 0 H1 in raw HTML."
    },
    {
      "id": "jsonld",
      "domain": "data",
      "title": "JSON-LD structured data is present",
      "weight": 4,
      "result": "fail",
      "evidence": "no JSON-LD",
      "recoverable": 4,
      "prompt": "Add a JSON-LD block to https://besthumanizer.app/ with @context https://schema.org and a @type that matches the page. Evidence: no JSON-LD."
    },
    {
      "id": "jsonld-valid",
      "domain": "data",
      "title": "Structured data validates",
      "weight": 4,
      "result": "fail",
      "evidence": "0 block(s) parse cleanly",
      "recoverable": 4,
      "prompt": "Fix the JSON-LD validation errors on https://besthumanizer.app/: 0 block(s) parse cleanly. Every block must parse and carry the required properties for its type."
    },
    {
      "id": "robots-valid",
      "domain": "access",
      "title": "robots.txt is reachable and valid",
      "weight": 3,
      "result": "fail",
      "evidence": "robots.txt HTTP 200, 0 groups",
      "recoverable": 3,
      "prompt": "Fix robots.txt at https://besthumanizer.app/robots.txt: it must return HTTP 200 with Content-Type text/plain, contain at least one \"User-agent: *\" group, and contain no HTML. Current evidence: robots.txt HTTP 200, 0 groups."
    },
    {
      "id": "real-404",
      "domain": "render",
      "title": "Unknown URLs return a real 404",
      "weight": 3,
      "result": "fail",
      "evidence": "unknown path returned 200 (soft 404)",
      "recoverable": 3,
      "prompt": "Return a real 404 status for unknown paths on https://besthumanizer.app instead of a 200 soft-404 page, and include links to real pages in the body. Evidence: unknown path returned 200 (soft 404)."
    },
    {
      "id": "no-js-only",
      "domain": "render",
      "title": "Primary content does not depend on client rendering",
      "weight": 3,
      "result": "fail",
      "evidence": "empty mount node detected",
      "recoverable": 3,
      "prompt": "Remove the JavaScript dependency for primary content on https://besthumanizer.app: the raw response must not be an empty mount node. Render on the server and hydrate. Evidence: empty mount node detected."
    },
    {
      "id": "one-h1",
      "domain": "structure",
      "title": "Exactly one H1 states the page subject",
      "weight": 3,
      "result": "fail",
      "evidence": "0 H1: \"\"",
      "recoverable": 3,
      "prompt": "Ensure exactly one non-empty H1 on https://besthumanizer.app/ that states the page subject; demote extra H1s to H2. Evidence: 0 H1: \"\"."
    },
    {
      "id": "canonical",
      "domain": "structure",
      "title": "Canonical points at itself",
      "weight": 3,
      "result": "fail",
      "evidence": "no canonical link",
      "recoverable": 3,
      "prompt": "Add a self-referencing rel=canonical to https://besthumanizer.app/ that matches the URL actually served (https, final host, no tracking params). Evidence: no canonical link."
    },
    {
      "id": "jsonld-type",
      "domain": "data",
      "title": "Declared type matches the page",
      "weight": 3,
      "result": "fail",
      "evidence": "no declared type",
      "recoverable": 3,
      "prompt": "Declare the correct schema type on https://besthumanizer.app/ (no declared type) — Product for a product page, Article for editorial, Service for a service, FAQPage when the page answers questions."
    },
    {
      "id": "org-entity",
      "domain": "data",
      "title": "Publisher entity is declared and linked",
      "weight": 3,
      "result": "fail",
      "evidence": "no Organization/Person node",
      "recoverable": 3,
      "prompt": "Add an Organization (or Person) JSON-LD node on https://besthumanizer.app with name, url, logo, and sameAs links to the profiles the brand actually owns. Evidence: no Organization/Person node."
    },
    {
      "id": "md-negotiation",
      "domain": "discovery",
      "title": "Content negotiation returns Markdown on request",
      "weight": 3,
      "result": "fail",
      "evidence": "Accept: text/markdown → text/html",
      "recoverable": 3,
      "prompt": "Support Accept: text/markdown on https://besthumanizer.app: return the Markdown mirror with Content-Type text/markdown and add Vary: Accept so caches stay separate. Evidence: Accept: text/markdown → text/html."
    },
    {
      "id": "site-search",
      "domain": "commerce",
      "title": "Search works through a GET URL",
      "weight": 3,
      "result": "fail",
      "evidence": "no GET search form",
      "recoverable": 3,
      "prompt": "Make site search on https://besthumanizer.app work over GET with a ?q= parameter that returns results in the server HTML, so an agent can construct a search URL. Evidence: no GET search form."
    },
    {
      "id": "bot-policy-deliberate",
      "domain": "access",
      "title": "AI bot policy is named, not inherited",
      "weight": 2,
      "result": "fail",
      "evidence": "0 named user-agent groups",
      "recoverable": 2,
      "prompt": "Name the AI crawlers explicitly in robots.txt for https://besthumanizer.app instead of relying on a wildcard, so each one has a deliberate policy. Evidence: 0 named user-agent groups."
    },
    {
      "id": "text-ratio",
      "domain": "render",
      "title": "Text-to-markup ratio is workable",
      "weight": 2,
      "result": "fail",
      "evidence": "text/markup 2.8%",
      "recoverable": 2,
      "prompt": "Raise the text-to-markup ratio on https://besthumanizer.app/ to at least 5%: trim wrapper divs and inline styles, and put the copy in plain elements. Evidence: text/markup 2.8%."
    },
    {
      "id": "llms-conform",
      "domain": "discovery",
      "title": "llms.txt follows the published format",
      "weight": 2,
      "result": "fail",
      "evidence": "H1 false, blockquote false, 0 H2, 0 links",
      "recoverable": 2,
      "prompt": "Restructure /llms.txt on https://besthumanizer.app to the published format: H1, blockquote summary under it, H2 sections whose bullets are [name](url) with optional \": note\" suffixes, and a ## Optional section for secondary links. Evidence: H1 false, blockquote false, 0 H2, 0 links."
    },
    {
      "id": "link-headers",
      "domain": "discovery",
      "title": "HTTP Link headers point at machine resources",
      "weight": 2,
      "result": "fail",
      "evidence": "no Link header",
      "recoverable": 2,
      "prompt": "Add Link response headers on https://besthumanizer.app pointing at its machine-readable resources: rel=\"describedby\" to /llms.txt, rel=\"api-catalog\" to /.well-known/api-catalog, rel=\"alternate\" to the Markdown mirror. Evidence: no Link header."
    },
    {
      "id": "content-signal",
      "domain": "discovery",
      "title": "Content usage preferences are declared",
      "weight": 2,
      "result": "fail",
      "evidence": "no Content-Signal line",
      "recoverable": 2,
      "prompt": "Add a Content-Signal line to robots.txt on https://besthumanizer.app, e.g. \"Content-Signal: search=yes, ai-input=yes, ai-train=no\", so usage terms are machine-readable. Evidence: no Content-Signal line."
    },
    {
      "id": "openapi",
      "domain": "interfaces",
      "title": "An OpenAPI description is published",
      "weight": 4,
      "result": "partial",
      "evidence": "openapi.json HTTP 200",
      "recoverable": 2,
      "prompt": "Publish /openapi.json on https://besthumanizer.app (OpenAPI 3.1) describing every public endpoint, with operation summaries and example responses. Evidence: openapi.json HTTP 200."
    },
    {
      "id": "api-catalog",
      "domain": "interfaces",
      "title": "An API catalog linkset is published",
      "weight": 2,
      "result": "fail",
      "evidence": "no linkset",
      "recoverable": 2,
      "prompt": "Publish /.well-known/api-catalog on https://besthumanizer.app as an RFC 9727 linkset pointing at the API, its OpenAPI description and its docs. Evidence: no linkset."
    },
    {
      "id": "mcp-card-fields",
      "domain": "interfaces",
      "title": "The MCP card carries the fields a client needs",
      "weight": 2,
      "result": "fail",
      "evidence": "0/4 required groups in the card",
      "recoverable": 2,
      "prompt": "Complete the MCP server card on https://besthumanizer.app: transport, endpoint URL, auth requirement, and every tool with a declared input schema and description. Evidence: 0/4 required groups in the card."
    },
    {
      "id": "fc-friendly",
      "domain": "interfaces",
      "title": "The API is easy to call from a model",
      "weight": 2,
      "result": "fail",
      "evidence": "0 described properties in the schema",
      "recoverable": 2,
      "prompt": "Simplify the API surface on https://besthumanizer.app for function calling: flat named parameters, enums instead of free-text where possible, and a description on every field in the OpenAPI spec. Evidence: 0 described properties in the schema."
    },
    {
      "id": "dev-portal",
      "domain": "interfaces",
      "title": "A developer portal is reachable from the homepage",
      "weight": 2,
      "result": "fail",
      "evidence": "no developer link",
      "recoverable": 2,
      "prompt": "Link the developer docs from the homepage and main navigation of https://besthumanizer.app — one hop, not three. Evidence: no developer link."
    },
    {
      "id": "agent-instruction",
      "domain": "interfaces",
      "title": "The site tells an agent when to use it",
      "weight": 2,
      "result": "fail",
      "evidence": "no when-to-use block",
      "recoverable": 2,
      "prompt": "Add a when-to-use block to https://besthumanizer.app (in llms.txt, the MCP card or the agent manifest) stating what the agent is for, what it must not do, and one worked call. Evidence: no when-to-use block."
    },
    {
      "id": "sources",
      "domain": "trust",
      "title": "Claims are checkable",
      "weight": 2,
      "result": "fail",
      "evidence": "0 outbound source link(s)",
      "recoverable": 2,
      "prompt": "Add outbound links to primary sources for the factual claims on https://besthumanizer.app/ — the operator documentation, study or specification, not another summary. Evidence: 0 outbound source link(s)."
    },
    {
      "id": "about-contact",
      "domain": "trust",
      "title": "About and contact are reachable",
      "weight": 2,
      "result": "fail",
      "evidence": "no about or contact link",
      "recoverable": 2,
      "prompt": "Publish a reachable About and Contact page on https://besthumanizer.app and link both from the footer. Evidence: no about or contact link."
    },
    {
      "id": "org-completeness",
      "domain": "trust",
      "title": "The organization node is complete",
      "weight": 2,
      "result": "fail",
      "evidence": "legalName missing",
      "recoverable": 2,
      "prompt": "Complete the Organization schema on https://besthumanizer.app with legalName, address, contactPoint, logo and sameAs. Evidence: legalName missing."
    },
    {
      "id": "llms-txt",
      "domain": "discovery",
      "title": "/llms.txt exists and is served as text",
      "weight": 3,
      "result": "partial",
      "evidence": "HTTP 200, 1967 bytes, text/html",
      "recoverable": 1.5,
      "prompt": "Publish /llms.txt on https://besthumanizer.app as text/plain: a single H1, a one-line blockquote summary, then H2 sections of [name](url) links to the pages that answer the most common questions. Evidence: HTTP 200, 1967 bytes, text/html."
    },
    {
      "id": "public-api",
      "domain": "interfaces",
      "title": "A public API answers without a human",
      "weight": 3,
      "result": "partial",
      "evidence": "no API declared",
      "recoverable": 1.5,
      "prompt": "Expose at least one unauthenticated read endpoint on https://besthumanizer.app that returns the site's core facts as JSON, and document it in the OpenAPI spec. Evidence: no API declared."
    },
    {
      "id": "author",
      "domain": "trust",
      "title": "A named, accountable author or publisher",
      "weight": 3,
      "result": "partial",
      "evidence": "no author signal",
      "recoverable": 1.5,
      "prompt": "Show a named author or accountable publisher on https://besthumanizer.app/, with a link to an about page that establishes who they are. Evidence: no author signal."
    },
    {
      "id": "training-policy",
      "domain": "access",
      "title": "Training policy is explicit",
      "weight": 1,
      "result": "fail",
      "evidence": "no explicit training policy",
      "recoverable": 1,
      "prompt": "State a training policy on https://besthumanizer.app: either name CCBot / Google-Extended / Bytespider explicitly in robots.txt, or add a Content-Signal line (search=yes, ai-input=yes, ai-train=no). Evidence: no explicit training policy."
    },
    {
      "id": "snippets-allowed",
      "domain": "access",
      "title": "Snippets and large image previews are allowed",
      "weight": 2,
      "result": "partial",
      "evidence": "snippet directives incomplete",
      "recoverable": 1,
      "prompt": "On https://besthumanizer.app set \"max-snippet:-1, max-image-preview:large\" in the meta robots tag or X-Robots-Tag so assistants can quote the page. Evidence: snippet directives incomplete."
    },
    {
      "id": "content-type",
      "domain": "render",
      "title": "Correct Content-Type and charset",
      "weight": 2,
      "result": "partial",
      "evidence": "content-type text/html",
      "recoverable": 1,
      "prompt": "Set correct Content-Type headers on https://besthumanizer.app: text/html; charset=utf-8 for pages, text/plain; charset=utf-8 for robots.txt, llms.txt and markdown mirrors. Evidence: content-type text/html."
    },
    {
      "id": "heading-order",
      "domain": "structure",
      "title": "Headings are sequential and descriptive",
      "weight": 2,
      "result": "partial",
      "evidence": "0 skipped level(s), 0 H2(s)",
      "recoverable": 1,
      "prompt": "Fix the heading outline on https://besthumanizer.app/: no skipped levels (H2 to H4), and make each H2 describe its section. Evidence: 0 skipped level(s), 0 H2(s)."
    },
    {
      "id": "landmarks",
      "domain": "structure",
      "title": "Content is separated by landmarks",
      "weight": 2,
      "result": "partial",
      "evidence": "no main/article element",
      "recoverable": 1,
      "prompt": "Wrap the primary copy of https://besthumanizer.app/ in <main> or <article> and keep nav/header/footer outside it, so a parser can isolate the content. Evidence: no main/article element."
    },
    {
      "id": "dates",
      "domain": "data",
      "title": "Published and modified dates are declared",
      "weight": 2,
      "result": "partial",
      "evidence": "no dates in schema",
      "recoverable": 1,
      "prompt": "Add datePublished and dateModified to the schema on https://besthumanizer.app/, kept in sync with the visible date. Evidence: no dates in schema."
    },
    {
      "id": "breadcrumb",
      "domain": "data",
      "title": "BreadcrumbList declares the page position",
      "weight": 1,
      "result": "fail",
      "evidence": "no BreadcrumbList",
      "recoverable": 1,
      "prompt": "Add a BreadcrumbList to https://besthumanizer.app/ whose items match the visible breadcrumb. Evidence: no BreadcrumbList."
    },
    {
      "id": "sitemap-lastmod",
      "domain": "discovery",
      "title": "Sitemap entries carry real lastmod",
      "weight": 2,
      "result": "partial",
      "evidence": "no lastmod",
      "recoverable": 1,
      "prompt": "Add truthful <lastmod> values to the sitemap on https://besthumanizer.app from real content timestamps. Evidence: no lastmod."
    },
    {
      "id": "md-mirror",
      "domain": "discovery",
      "title": "A Markdown mirror of the page exists",
      "weight": 2,
      "result": "partial",
      "evidence": "https://besthumanizer.app.md → HTTP 200",
      "recoverable": 1,
      "prompt": "Publish a Markdown mirror for each page on https://besthumanizer.app: serve /<path>.md with Content-Type text/markdown and the same content as the HTML. Evidence: https://besthumanizer.app.md → HTTP 200."
    },
    {
      "id": "md-alternate",
      "domain": "discovery",
      "title": "HTML advertises its Markdown twin",
      "weight": 1,
      "result": "fail",
      "evidence": "no <link rel=alternate type=text/markdown>",
      "recoverable": 1,
      "prompt": "Add <link rel=\"alternate\" type=\"text/markdown\" href=\"/<path>.md\"> to the head of https://besthumanizer.app/. Evidence: no <link rel=alternate type=text/markdown>."
    },
    {
      "id": "dns-aid",
      "domain": "discovery",
      "title": "DNS advertises agent endpoints",
      "weight": 1,
      "result": "fail",
      "evidence": "no _agents TXT record",
      "recoverable": 1,
      "prompt": "Publish a DNS discovery record for https://besthumanizer.app (a TXT record listing the agent endpoints the site exposes) so an agent learns the surface before fetching a page. Evidence: no _agents TXT record."
    },
    {
      "id": "describedby",
      "domain": "discovery",
      "title": "The page declares the index that covers it",
      "weight": 1,
      "result": "fail",
      "evidence": "rel=describedby to the index",
      "recoverable": 1,
      "prompt": "Add a rel=\"describedby\" link to the covering /llms.txt from every page on https://besthumanizer.app so an agent can widen from one page to the index. Evidence: rel=describedby to the index."
    },
    {
      "id": "webmcp",
      "domain": "interfaces",
      "title": "Site actions are exposed to in-browser agents",
      "weight": 1,
      "result": "fail",
      "evidence": "no WebMCP registration",
      "recoverable": 1,
      "prompt": "Register the site's real actions (search, filter, add to cart) as WebMCP tools on https://besthumanizer.app so a browser agent can call them without guessing at the DOM. Evidence: no WebMCP registration."
    },
    {
      "id": "nlweb-ask",
      "domain": "interfaces",
      "title": "A natural-language query endpoint answers",
      "weight": 1,
      "result": "fail",
      "evidence": "/ask → HTTP 200, text/html",
      "recoverable": 1,
      "prompt": "Expose a natural-language query endpoint at https://besthumanizer.app/ask that accepts ?query= and returns JSON (answer plus result URLs), so an agent can ask the site directly instead of crawling it. Evidence: /ask → HTTP 200, text/html."
    },
    {
      "id": "a2ui",
      "domain": "interfaces",
      "title": "The interface is declared for agent-driven UI",
      "weight": 1,
      "result": "fail",
      "evidence": "no A2UI declaration",
      "recoverable": 1,
      "prompt": "Declare an agent-driven UI surface on https://besthumanizer.app: mark the page (or publish a manifest) as A2UI so a reviewing agent knows the controls are renderable from a declared schema rather than inferred from the DOM. Evidence: no A2UI declaration."
    },
    {
      "id": "rate-limit-headers",
      "domain": "interfaces",
      "title": "Rate limits are declared in headers",
      "weight": 1,
      "result": "fail",
      "evidence": "no rate-limit headers",
      "recoverable": 1,
      "prompt": "Emit RateLimit-Limit / RateLimit-Remaining / RateLimit-Reset (and Retry-After on 429) from the API on https://besthumanizer.app. Evidence: no rate-limit headers."
    },
    {
      "id": "idempotency",
      "domain": "interfaces",
      "title": "Writes are retry-safe",
      "weight": 1,
      "result": "fail",
      "evidence": "Idempotency-Key documented",
      "recoverable": 1,
      "prompt": "Accept an Idempotency-Key header on mutating endpoints of https://besthumanizer.app and document it, so an agent can retry without duplicating work. Evidence: Idempotency-Key documented."
    },
    {
      "id": "payment-negotiation",
      "domain": "interfaces",
      "title": "Paid access is discoverable by an agent",
      "weight": 1,
      "result": "fail",
      "evidence": "x402 discovery HTTP 404",
      "recoverable": 1,
      "prompt": "Make paid access discoverable on https://besthumanizer.app: return HTTP 402 with a machine-readable list of accepted payment options, or publish a machine-readable price list. Evidence: x402 discovery HTTP 404."
    },
    {
      "id": "visible-date",
      "domain": "trust",
      "title": "A visible publish or update date",
      "weight": 2,
      "result": "partial",
      "evidence": "no visible date",
      "recoverable": 1,
      "prompt": "Show a visible publish or last-updated date on https://besthumanizer.app/ that matches dateModified in the schema. Evidence: no visible date."
    },
    {
      "id": "https",
      "domain": "trust",
      "title": "Every page is served over HTTPS",
      "weight": 2,
      "result": "partial",
      "evidence": "https origin, http:// answered 200",
      "recoverable": 1,
      "prompt": "Serve https://besthumanizer.app only over HTTPS and 301 every http:// URL to https://. Evidence: https origin, http:// answered 200."
    },
    {
      "id": "security-headers",
      "domain": "trust",
      "title": "Baseline security headers are set",
      "weight": 1,
      "result": "fail",
      "evidence": "HSTS no, nosniff no",
      "recoverable": 1,
      "prompt": "Set Strict-Transport-Security, X-Content-Type-Options: nosniff and Referrer-Policy: strict-origin-when-cross-origin on https://besthumanizer.app. Evidence: HSTS no, nosniff no."
    },
    {
      "id": "crawl-budget",
      "domain": "access",
      "title": "Infrastructure paths are excluded from crawl budget",
      "weight": 1,
      "result": "partial",
      "evidence": "no /api/ or /cdn-cgi/ exclusion",
      "recoverable": 0.5,
      "prompt": "On https://besthumanizer.app exclude infrastructure from crawl budget: Disallow /api/ and /cdn-cgi/, then re-Allow /static/ and your badge/sitemap paths. Evidence: no /api/ or /cdn-cgi/ exclusion."
    },
    {
      "id": "compression",
      "domain": "render",
      "title": "Text responses are compressed",
      "weight": 1,
      "result": "partial",
      "evidence": "no compression header relayed",
      "recoverable": 0.5,
      "prompt": "Enable Brotli/gzip compression for HTML and JSON on https://besthumanizer.app. Evidence: no compression header relayed."
    },
    {
      "id": "sitemap-md",
      "domain": "discovery",
      "title": "A Markdown sitemap lists the pages in one read",
      "weight": 1,
      "result": "partial",
      "evidence": "sitemap.md lists 0 link(s)",
      "recoverable": 0.5,
      "prompt": "Publish /sitemap.md on https://besthumanizer.app: a Markdown list of the site's pages as [title](url), one line each, so an agent gets the whole map in one cheap request. Evidence: sitemap.md lists 0 link(s)."
    },
    {
      "id": "agents-md-depth",
      "domain": "discovery",
      "title": "The skill file is substantive",
      "weight": 1,
      "result": "partial",
      "evidence": "0/3 of install, configuration, examples",
      "recoverable": 0.5,
      "prompt": "Deepen /AGENTS.md on https://besthumanizer.app to include install steps, configuration, and at least one complete usage example. Evidence: 0/3 of install, configuration, examples."
    },
    {
      "id": "agents-json",
      "domain": "interfaces",
      "title": "A wildcard agent manifest is published",
      "weight": 1,
      "result": "partial",
      "evidence": "0 agent(s) enumerated",
      "recoverable": 0.5,
      "prompt": "Publish /.well-known/agents.json on https://besthumanizer.app: a JSON document naming each agent or action the site accepts, with the URL to call and the input it expects. Evidence: 0 agent(s) enumerated."
    },
    {
      "id": "agent-permissions",
      "domain": "interfaces",
      "title": "Per-action permissions are declared",
      "weight": 1,
      "result": "partial",
      "evidence": "permissions file has no scope/consent keys",
      "recoverable": 0.5,
      "prompt": "Publish /.well-known/agent-permissions.json on https://besthumanizer.app mapping each action an agent may take to the scope it needs and whether a human must confirm it. Evidence: permissions file has no scope/consent keys."
    },
    {
      "id": "web-bot-auth",
      "domain": "access",
      "title": "The site publishes its own bot signing keys",
      "weight": 1,
      "result": "partial",
      "evidence": "0 signing key(s)",
      "recoverable": 0.5,
      "prompt": "Publish a signing-key directory at https://besthumanizer.app/.well-known/http-message-signatures-directory so another site can verify requests this site's bots send. Evidence: 0 signing key(s)."
    }
  ],
  "counts": {
    "total": 120,
    "applicable": 99,
    "pass": 42,
    "partial": 20,
    "fail": 37,
    "notApplicable": 21,
    "inconclusive": 0
  },
  "durationMs": 1310,
  "cache": {
    "hit": true,
    "ageMs": 967192,
    "ttlMs": 21600000
  }
}
