[
  {
    "id": "wcag-1.1.1-text-alternative",
    "version": 1,
    "domains": ["accessibility"],
    "title": "Text alternatives",
    "instruction": "Give informative images a text alternative. Mark decorative images so assistive technology can ignore them.",
    "rationale": "WCAG 2.2 success criterion 1.1.1, level A, with the decorative, control, and sensory exceptions in the quick reference.",
    "authorityIds": ["src-wcag22-quickref"],
    "authorityClass": "standard",
    "applicability": "always",
    "stages": ["representative", "pages", "browser"],
    "verificationMethod": "DOM scan for missing alt, plus manual review of whether the text is equivalent.",
    "verifierId": "text-alternative",
    "severityPolicy": "blocker",
    "exceptions": ["Decorative images with an empty alt", "CAPTCHA and sensory exceptions require a described purpose"],
    "repairOwner": "pages",
    "implementationStatus": "fixture-tested"
  },
  {
    "id": "wcag-1.4.3-contrast",
    "version": 1,
    "domains": ["accessibility"],
    "title": "Contrast minimum",
    "instruction": "Body text needs at least 4.5:1. Large text needs at least 3:1. Do not collapse every contrast rule into one number.",
    "rationale": "WCAG 2.2 success criterion 1.4.3, level AA.",
    "authorityIds": ["src-wcag22-quickref"],
    "authorityClass": "standard",
    "applicability": "always",
    "stages": ["direction", "representative", "pages"],
    "verificationMethod": "Computed contrast for explicit color pairs.",
    "verifierId": "contrast",
    "severityPolicy": "blocker",
    "exceptions": ["Large text at 3:1", "Incidental text", "Disabled controls", "Logotypes"],
    "repairOwner": "pages",
    "implementationStatus": "fixture-tested"
  },
  {
    "id": "wcag-2.1.1-keyboard",
    "version": 1,
    "domains": ["accessibility"],
    "title": "Keyboard operation",
    "instruction": "The primary contact path must be operable by keyboard and must not trap focus.",
    "rationale": "WCAG 2.2 success criterion 2.1.1, level A.",
    "authorityIds": ["src-wcag22-quickref"],
    "authorityClass": "standard",
    "applicability": "contact-path",
    "stages": ["representative", "pages", "browser"],
    "verificationMethod": "Detect scripts that cancel Tab without an exit. Full keyboard testing remains manual where the verifier does not run a browser.",
    "verifierId": "keyboard-trap",
    "severityPolicy": "blocker",
    "exceptions": ["A modal may hold Tab while it is open if Escape or an explicit close control releases it"],
    "repairOwner": "pages",
    "implementationStatus": "fixture-tested"
  },
  {
    "id": "wcag-2.4.11-focus-not-obscured",
    "version": 1,
    "domains": ["accessibility"],
    "title": "Focus not obscured",
    "instruction": "Sticky or fixed interface must not cover the focused control. Reserve scroll padding for the sticky region.",
    "rationale": "WCAG 2.2 success criterion 2.4.11, level AA.",
    "authorityIds": ["src-wcag22-quickref"],
    "authorityClass": "standard",
    "applicability": "always",
    "stages": ["representative", "pages"],
    "verificationMethod": "Static check for sticky or fixed positioning without scroll-padding-top.",
    "verifierId": "focus-offset",
    "severityPolicy": "blocker",
    "exceptions": ["Author-declared focus exception with scroll-padding-top"],
    "repairOwner": "pages",
    "implementationStatus": "fixture-tested"
  },
  {
    "id": "wcag-3.3.2-labels",
    "version": 1,
    "domains": ["accessibility"],
    "title": "Form labels",
    "instruction": "Name every contact field in a way that states its purpose. Placeholder text is not the name.",
    "rationale": "WCAG 2.2 success criterion 3.3.2, level A.",
    "authorityIds": ["src-wcag22-quickref", "src-wai-aria-apg"],
    "authorityClass": "standard",
    "applicability": "contact-path",
    "stages": ["copy", "representative", "pages"],
    "verificationMethod": "Accessible name from label, aria-label, or aria-labelledby.",
    "verifierId": "form-name",
    "severityPolicy": "blocker",
    "exceptions": ["Submit buttons with their own text"],
    "repairOwner": "pages",
    "implementationStatus": "fixture-tested"
  },
  {
    "id": "wcag-3.3.1-error-identification",
    "version": 1,
    "domains": ["accessibility"],
    "title": "Error identification",
    "instruction": "When a required contact field can fail, identify the error in text associated with the field.",
    "rationale": "WCAG 2.2 success criterion 3.3.1, level A, together with status feedback in 4.1.3.",
    "authorityIds": ["src-wcag22-quickref"],
    "authorityClass": "standard",
    "applicability": "contact-path",
    "stages": ["representative", "pages", "browser"],
    "verificationMethod": "Required controls must point at an existing described-by or error element.",
    "verifierId": "form-errors",
    "severityPolicy": "blocker",
    "exceptions": [],
    "repairOwner": "pages",
    "implementationStatus": "fixture-tested"
  },
  {
    "id": "wcag-manual-screen-reader",
    "version": 1,
    "domains": ["accessibility"],
    "title": "Screen reader evaluation",
    "instruction": "Record that screen-reader behavior was not claimed from an accessibility-tree snapshot.",
    "rationale": "W3C evaluation guidance says tools cannot by themselves determine conformance.",
    "authorityIds": ["src-wai-evaluate"],
    "authorityClass": "standard",
    "applicability": "always",
    "stages": ["browser", "preview"],
    "verificationMethod": "Manual. Pending manual coverage is disclosed and is not itself a universal launch deadlock.",
    "verifierId": null,
    "severityPolicy": "manual-pending",
    "exceptions": [],
    "repairOwner": "browser",
    "implementationStatus": "documented"
  },
  {
    "id": "widget-carousel-keyboard",
    "version": 1,
    "domains": ["accessibility"],
    "title": "Carousel keyboard behavior",
    "instruction": "When a carousel is used, previous and next controls need names and keyboard operation. Do not autoplay.",
    "rationale": "APG widget practice. Irrelevant when the page has no carousel.",
    "authorityIds": ["src-wai-aria-apg"],
    "authorityClass": "official-platform",
    "applicability": "widget",
    "stages": ["pages", "browser"],
    "verificationMethod": "Load only when the carousel island is present. Kernel carousel uses buttons and arrow keys and does not autoplay.",
    "verifierId": "island-audit",
    "severityPolicy": "warning",
    "exceptions": [],
    "repairOwner": "pages",
    "implementationStatus": "documented",
    "widget": "carousel"
  },
  {
    "id": "nng-plain-language-and-recovery",
    "version": 1,
    "domains": ["usability"],
    "title": "Plain language and error recovery",
    "instruction": "Use the visitor's words for the next step. If a form fails, say what went wrong and how to fix it.",
    "rationale": "Nielsen heuristics 2 and 9. They are not a blocking standard by themselves.",
    "authorityIds": ["src-nng-heuristics"],
    "authorityClass": "research-heuristic",
    "applicability": "contact-path",
    "stages": ["copy", "representative"],
    "verificationMethod": "Advisory. A semantic concern becomes a failure only after a verifier or a human review confirms it.",
    "verifierId": null,
    "severityPolicy": "warning",
    "exceptions": [],
    "repairOwner": "copy",
    "implementationStatus": "documented"
  },
  {
    "id": "seo-indexing-intent",
    "version": 1,
    "domains": ["seo"],
    "title": "Indexing intent",
    "instruction": "Preview may be noindex. Production noindex on a page meant to be found is a launch blocker.",
    "rationale": "Google documents opting out of indexing. Vitris treats an accidental production noindex as a technical SEO defect.",
    "authorityIds": ["src-google-seo-starter"],
    "authorityClass": "official-platform",
    "applicability": "always",
    "stages": ["preview"],
    "verificationMethod": "Compare robots meta with environment and the page's indexing intent.",
    "verifierId": "indexing",
    "severityPolicy": "blocker",
    "exceptions": ["Pages explicitly marked nonindexable"],
    "repairOwner": "pages",
    "implementationStatus": "fixture-tested"
  },
  {
    "id": "seo-canonical-origin",
    "version": 1,
    "domains": ["seo"],
    "title": "Canonical origin",
    "instruction": "Production canonical URLs use the configured public origin and a route that exists.",
    "rationale": "Google's starter guide treats a preferred URL as the way to handle duplicates. A wrong host is a technical defect.",
    "authorityIds": ["src-google-seo-starter"],
    "authorityClass": "official-platform",
    "applicability": "always",
    "stages": ["preview"],
    "verificationMethod": "Compare canonical href with publicOrigin and built routes.",
    "verifierId": "canonical",
    "severityPolicy": "blocker",
    "exceptions": ["Missing publicOrigin is a preview warning and a production blocker"],
    "repairOwner": "launch",
    "implementationStatus": "fixture-tested"
  },
  {
    "id": "seo-sitemap-exclusions",
    "version": 1,
    "domains": ["seo"],
    "title": "Sitemap exclusions",
    "instruction": "Production sitemaps use absolute URLs and omit redirects, retired URLs, and intentionally nonindexable pages.",
    "rationale": "A sitemap should list URLs you want crawled. It is not proof that every historical URL was discovered.",
    "authorityIds": ["src-google-seo-starter"],
    "authorityClass": "official-platform",
    "applicability": "always",
    "stages": ["preview"],
    "verificationMethod": "Compare sitemap loc values with route dispositions.",
    "verifierId": "sitemap",
    "severityPolicy": "blocker",
    "exceptions": [],
    "repairOwner": "compiler",
    "implementationStatus": "fixture-tested"
  },
  {
    "id": "seo-redirect-integrity",
    "version": 1,
    "domains": ["seo"],
    "title": "Redirect integrity",
    "instruction": "Every discovered material URL needs keep, redirect, consolidate, or retire. Reject loops, missing destinations, and accidental homepage redirects.",
    "rationale": "URL changes need an explicit destination. Harvest coverage is not a claim that every old URL was found.",
    "authorityIds": ["src-google-site-move"],
    "authorityClass": "official-platform",
    "applicability": "always",
    "stages": ["storyboard", "preview"],
    "verificationMethod": "Graph walk of storyboard route dispositions.",
    "verifierId": "redirects",
    "severityPolicy": "blocker",
    "exceptions": ["An explicit intentional homepage redirect"],
    "repairOwner": "storyboard",
    "implementationStatus": "fixture-tested"
  },
  {
    "id": "seo-structured-data-consistency",
    "version": 1,
    "domains": ["seo"],
    "title": "Structured data consistency",
    "instruction": "Use Organization unless a real address supports LocalBusiness. The entity must match visible content. Do not invent ratings or service areas.",
    "rationale": "Structured data validity, visible consistency, and rich-result eligibility are different checks.",
    "authorityIds": ["src-google-seo-starter"],
    "authorityClass": "official-platform",
    "applicability": "always",
    "stages": ["pages", "preview"],
    "verificationMethod": "Parse JSON-LD and compare name, type, and address with visible facts.",
    "verifierId": "structured-data",
    "severityPolicy": "blocker",
    "exceptions": ["Eligibility for a rich result is not claimed"],
    "repairOwner": "compiler",
    "implementationStatus": "fixture-tested"
  },
  {
    "id": "geo-visible-answers",
    "version": 1,
    "domains": ["geo"],
    "title": "Answer the visitor in visible text",
    "instruction": "Answer the questions a visitor actually has, in the page, with the business and services identified from evidence. Do not add FAQ blocks, fake credentials, or city pages to chase citations.",
    "rationale": "Google says its AI features rely on existing SEO foundations and do not need special files. Other providers are not covered.",
    "authorityIds": ["src-google-ai-features"],
    "authorityClass": "official-platform",
    "applicability": "service-site",
    "stages": ["copy"],
    "verificationMethod": "Advisory. No llms.txt launch requirement.",
    "verifierId": null,
    "severityPolicy": "warning",
    "exceptions": ["Experimental crawler files require a separate reviewed policy"],
    "repairOwner": "copy",
    "implementationStatus": "documented"
  },
  {
    "id": "perf-transfer-budget",
    "version": 1,
    "domains": ["performance"],
    "title": "Starting transfer budget",
    "instruction": "Keep first-party JavaScript and CSS within the versioned Vitris budgets. A broken or empty shipped resource blocks. Lab timing stays a warning while variance is calibrated.",
    "rationale": "These limits are Vitris policy. Core Web Vitals remain field targets and are unknown until measured.",
    "authorityIds": ["src-vitris-transfer-budget", "src-web-vitals"],
    "authorityClass": "project-policy",
    "applicability": "always",
    "stages": ["pages", "browser", "preview"],
    "verificationMethod": "Gzip and byte totals. Unavailable lab or field data stays unknown.",
    "verifierId": "transfer-budget",
    "severityPolicy": "blocker",
    "exceptions": ["A stated media-heavy exception"],
    "repairOwner": "pages",
    "implementationStatus": "fixture-tested"
  },
  {
    "id": "perf-field-cwv",
    "version": 1,
    "domains": ["performance"],
    "title": "Field Core Web Vitals",
    "instruction": "Do not mark LCP, INP, or CLS passed from a missing field sample or a single lab run.",
    "rationale": "web.dev defines the 75th percentile field targets. Lab measurement is prelaunch evidence only.",
    "authorityIds": ["src-web-vitals"],
    "authorityClass": "official-platform",
    "applicability": "always",
    "stages": ["browser", "preview"],
    "verificationMethod": "Record unknown when field data or the lab runner is absent.",
    "verifierId": "field-cwv",
    "severityPolicy": "warning",
    "exceptions": [],
    "repairOwner": "browser",
    "implementationStatus": "fixture-tested"
  },
  {
    "id": "vitris-launch-evidence-binding",
    "version": 1,
    "domains": ["publication"],
    "title": "Bound launch evidence",
    "instruction": "Production publication requires evidence tied to the same build and policy. A passed flag in an unbound JSON file is not evidence.",
    "rationale": "Supported CLI, site script, CI, and Worker paths share this rule. Manual uploads outside those paths are not prevented.",
    "authorityIds": ["src-vitris-transfer-budget"],
    "authorityClass": "project-policy",
    "applicability": "always",
    "stages": ["preview"],
    "verificationMethod": "Shared launch decision. Worker pages upload fails closed without a trusted receipt for the same build hash.",
    "verifierId": "launch-policy",
    "severityPolicy": "blocker",
    "exceptions": ["An explicit scoped override is reported as waived, not passed"],
    "repairOwner": "launch",
    "implementationStatus": "fixture-tested"
  },
  {
    "id": "teaching-creative-freedom",
    "version": 1,
    "domains": ["design"],
    "title": "Creative freedom",
    "instruction": "Accept restrained pages, purposeful repetition, and alternate accessible compositions. Flag novelty only when it creates a visitor or implementation defect.",
    "rationale": "Teaching guidance. It does not outrank a confirmed accessibility, functionality, or indexing failure.",
    "authorityIds": ["src-nng-heuristics"],
    "authorityClass": "teaching",
    "applicability": "service-site",
    "stages": ["direction", "representative"],
    "verificationMethod": "Critic must name a visitor problem, business mismatch, or defect.",
    "verifierId": "creative-freedom",
    "severityPolicy": "warning",
    "exceptions": [],
    "repairOwner": "direction",
    "implementationStatus": "fixture-tested"
  }
]
