{
  "rubricVersion": "rubric-v1.1",
  "minDecidedForGrade": 5,
  "profiles": [
    "general",
    "dev-platform",
    "docs",
    "publisher"
  ],
  "scoring": "score = 100 * passed-weight / decided-weight; checks that cannot be measured or do not apply are excluded from the denominator, never counted as failures",
  "contract": {
    "idStability": "Check ids are stable: an id never changes meaning. Gate CI on ids, not names or wording.",
    "versioning": "rubricVersion names the frozen rubric this catalog reflects. Weights, the check list, and profile gating change only with a new rubric version, never silently within one; the frozen files are SHA-256 hash-guarded in CI.",
    "deprecation": "Checks are never removed or re-weighted within a rubric version. A retired or re-weighted check appears only under a new rubricVersion, so pin the version if you depend on exact weights."
  },
  "checks": [
    {
      "id": "AC-ROB-02",
      "name": "robots.txt is readable and sane",
      "category": "access",
      "weight": 12,
      "why": "robots.txt is the first file every agent reads; if it is broken or self-defeating, everything downstream suffers.",
      "fix": "Make robots.txt reachable and remove Disallow rules that block your own agent files (/llms.txt, /sitemap.xml, /.well-known/).",
      "profiles": [
        "general",
        "dev-platform",
        "docs",
        "publisher"
      ]
    },
    {
      "id": "AC-UAD-02",
      "name": "Agents can get in",
      "category": "access",
      "weight": 12,
      "why": "This is the bottom line: when an AI agent knocks, does it get the page?",
      "fix": "Review your WAF / bot-management rules for the named agents your policy intends to allow.",
      "profiles": [
        "general",
        "dev-platform",
        "docs",
        "publisher"
      ]
    },
    {
      "id": "AC-REN-01",
      "name": "Content is readable without JavaScript",
      "category": "rendering",
      "weight": 10,
      "why": "Most agents read raw HTML and never run your JavaScript; an empty shell means you are invisible.",
      "fix": "Server-render your primary content; an empty SPA shell is invisible to most agents.",
      "profiles": [
        "general",
        "dev-platform",
        "docs",
        "publisher"
      ]
    },
    {
      "id": "AC-LLM-01",
      "name": "llms.txt exists",
      "category": "llms",
      "weight": 8,
      "why": "llms.txt is the welcome note AI systems look for first; sites that have one get summarized on their own terms.",
      "fix": "Publish /llms.txt: a short markdown guide that tells AI systems what your site is and where the important pages are.",
      "profiles": [
        "general",
        "dev-platform",
        "docs",
        "publisher"
      ]
    },
    {
      "id": "AC-REN-02",
      "name": "Markdown when asked (content negotiation)",
      "category": "rendering",
      "weight": 8,
      "why": "Markdown is roughly 10x cheaper for an AI to read than rendered HTML.",
      "fix": "Return text/markdown when a request sends Accept: text/markdown.",
      "profiles": [
        "general",
        "dev-platform",
        "docs",
        "publisher"
      ]
    },
    {
      "id": "AC-UAD-03",
      "name": "Agents get the same content as a browser",
      "category": "access",
      "weight": 6,
      "why": "Some sites let agents in but serve them a stripped-down page; the agent then answers from thin content.",
      "fix": "Serve agents the same primary content a browser receives; avoid UA-conditional thin pages.",
      "profiles": [
        "general",
        "dev-platform",
        "docs",
        "publisher"
      ]
    },
    {
      "id": "AC-ROB-04",
      "name": "No page-level AI bans (noindex / noai)",
      "category": "access",
      "weight": 5,
      "why": "A single meta tag can quietly remove your pages from AI answers.",
      "fix": "Remove noindex/noai meta tags or X-Robots-Tag headers from pages you want AI systems to read.",
      "profiles": [
        "general",
        "dev-platform",
        "docs",
        "publisher"
      ]
    },
    {
      "id": "AC-DIS-01",
      "name": "Sitemap exists and is valid XML",
      "category": "discovery",
      "weight": 5,
      "why": "The sitemap is how agents and crawlers enumerate what you publish.",
      "fix": "Publish /sitemap.xml as real XML and reference it from robots.txt.",
      "profiles": [
        "general",
        "dev-platform",
        "docs",
        "publisher"
      ]
    },
    {
      "id": "AC-LLM-02",
      "name": "llms.txt starts with a title",
      "category": "llms",
      "weight": 5,
      "why": "Without the title line, many parsers treat the file as malformed.",
      "fix": "Start llms.txt with a single markdown H1 (# Your site name).",
      "profiles": [
        "general",
        "dev-platform",
        "docs",
        "publisher"
      ]
    },
    {
      "id": "AC-REN-09",
      "name": "Missing pages return an honest 404",
      "category": "rendering",
      "weight": 5,
      "why": "A page that returns 200 OK while actually showing a not-found message sends agents in circles.",
      "fix": "Return real 404/410 status codes; a 200 \"not found\" page poisons agent navigation.",
      "profiles": [
        "general",
        "dev-platform",
        "docs",
        "publisher"
      ]
    },
    {
      "id": "AC-REN-10",
      "name": "No login, consent wall, or paywall in front of content",
      "category": "rendering",
      "weight": 5,
      "why": "Whatever sits in front of your content is all an agent will ever see of it.",
      "fix": "Keep primary content readable before any interstitial, or accept that agents cannot read it.",
      "profiles": [
        "general",
        "dev-platform",
        "docs",
        "publisher"
      ]
    },
    {
      "id": "AC-REN-11",
      "name": "Clean status and redirects",
      "category": "rendering",
      "weight": 5,
      "why": "Redirect chains and cross-host hops make agents abort the fetch.",
      "fix": "Serve the homepage with a direct 200 or one clean redirect hop, on the same host.",
      "profiles": [
        "general",
        "dev-platform",
        "docs",
        "publisher"
      ]
    },
    {
      "id": "AC-API-01",
      "name": "OpenAPI spec published (developer platforms)",
      "category": "api",
      "weight": 5,
      "why": "An agent integrates with a developer platform through its API; the published spec is how it learns the endpoints.",
      "fix": "Publish your API description at a well-known path (e.g. /openapi.json).",
      "profiles": [
        "dev-platform",
        "docs"
      ]
    },
    {
      "id": "AC-SD-01",
      "name": "Structured data (JSON-LD) present",
      "category": "structured-data",
      "weight": 4,
      "why": "Structured data lets agents quote your facts precisely instead of guessing from prose.",
      "fix": "Add typed schema.org JSON-LD so agents can read facts without parsing prose.",
      "profiles": [
        "general",
        "dev-platform",
        "docs",
        "publisher"
      ]
    },
    {
      "id": "AC-ROB-03",
      "name": "Live assistants are not silently denied",
      "category": "access",
      "weight": 3,
      "why": "When a person asks their AI about your site mid-conversation, this is the request that fails.",
      "fix": "Allow the user-fetch agents (ChatGPT-User, Claude-User, Perplexity-User) in your WAF, or declare the block in robots.txt so it is at least visible policy.",
      "profiles": [
        "general",
        "dev-platform",
        "docs",
        "publisher"
      ]
    },
    {
      "id": "AC-ROB-06",
      "name": "No CAPTCHA on the front door",
      "category": "access",
      "weight": 3,
      "why": "Agents cannot solve CAPTCHAs; one on the front door turns every agent away.",
      "fix": "Keep CAPTCHA on abuse-prone actions (signup, login), not on the page an agent must read first.",
      "profiles": [
        "general",
        "dev-platform",
        "docs",
        "publisher"
      ]
    },
    {
      "id": "AC-LLM-03",
      "name": "llms.txt fits an AI's reading budget",
      "category": "llms",
      "weight": 3,
      "why": "Agents read with a fixed budget; an oversized file gets truncated mid-sentence.",
      "fix": "Keep llms.txt concise; link out instead of inlining everything.",
      "profiles": [
        "general",
        "dev-platform",
        "docs",
        "publisher"
      ]
    },
    {
      "id": "AC-LLM-04",
      "name": "llms.txt links actually resolve",
      "category": "llms",
      "weight": 3,
      "why": "Agents follow these links literally; each dead one is a dead end in a conversation.",
      "fix": "Fix or remove dead links in llms.txt; agents follow them literally.",
      "profiles": [
        "general",
        "dev-platform",
        "docs",
        "publisher"
      ]
    },
    {
      "id": "AC-REN-03",
      "name": "A markdown mirror is fetchable",
      "category": "rendering",
      "weight": 3,
      "why": "A markdown twin gives agents a clean, cheap version of each page.",
      "fix": "Serve a .md twin of key pages (e.g. /page.md or /page/index.md).",
      "profiles": [
        "dev-platform",
        "docs"
      ]
    },
    {
      "id": "AC-REN-07",
      "name": "Page weight fits an AI's budget",
      "category": "rendering",
      "weight": 3,
      "why": "Oversized pages get cut off mid-read; whatever falls past the cut is never seen.",
      "fix": "Trim raw HTML; huge pages get truncated mid-read by agents with fixed context budgets.",
      "profiles": [
        "general",
        "dev-platform",
        "docs",
        "publisher"
      ]
    },
    {
      "id": "AC-DIS-04",
      "name": "Agent files served with correct content types",
      "category": "discovery",
      "weight": 2,
      "why": "A file served with the wrong type gets misread or discarded by strict clients.",
      "fix": "Serve llms.txt as text/plain or text/markdown, sitemaps as XML, OpenAPI as JSON.",
      "profiles": [
        "general",
        "dev-platform",
        "docs",
        "publisher"
      ]
    },
    {
      "id": "AC-REN-04",
      "name": "Markdown alternate is declared",
      "category": "rendering",
      "weight": 2,
      "why": "The declaration is how an agent discovers your markdown version exists.",
      "fix": "Add <link rel=\"alternate\" type=\"text/markdown\" href=\"...\"> to point agents at the markdown version.",
      "profiles": [
        "dev-platform",
        "docs"
      ]
    },
    {
      "id": "AC-REN-12",
      "name": "Forms work without JavaScript",
      "category": "rendering",
      "weight": 2,
      "why": "A form without action and method attributes only works with JavaScript, which most agents never run.",
      "fix": "Give forms real action= and method= attributes so non-browser clients can submit them.",
      "profiles": [
        "general",
        "dev-platform",
        "docs",
        "publisher"
      ]
    }
  ]
}