{
  "version": "2026-09-06",
  "generatedAt": "2026-09-06T19:16:53.710Z",
  "source": "generated by aiseo-saas scripts/export-plugin-rules.js from utils/scoreService.js, htmlChecker.js, analyzerService.js, httpChecker.js, robotsChecker.js, sitemapChecker.js, previewViewModel.js",
  "thresholds": {
    "titleMin": 30,
    "titleMax": 60,
    "descriptionMax": 160,
    "altCoverageMinPercent": 80,
    "freshnessDays": 180,
    "securityHeadersMin": 4,
    "securityHeadersTotal": 6,
    "maxRedirectHops": 2,
    "brokenLinksMax": 0,
    "maxBrokenLinkCandidates": 50,
    "mixedContentMaxUrls": 20,
    "productSchemaMinPercent": 75,
    "productCompletenessChecks": 8,
    "aiCrawlerMajorsBlockedMax": 1,
    "explicitPolicyMinKeyBots": 3,
    "sitemapFreshnessDays": 180
  },
  "comparisons": {
    "titleMin": "titleLength >= titleMin (a missing title never fails the length check)",
    "titleMax": "titleLength <= titleMax",
    "descriptionMax": "descriptionLength <= descriptionMax (first description tag only)",
    "altCoverageMinPercent": "imageCount == 0 || round(imagesWithAltAttribute / imageCount * 100) >= altCoverageMinPercent (an empty alt attribute counts as covered)",
    "freshnessDays": "daysSinceModified < freshnessDays with daysSinceModified = round((now - date) / 1 day) and date = dateModified if present else datePublished (dateModified wins even when older); dates come from JSON-LD (any nesting, first match), then meta article:published_time / article:modified_time, then the first <time datetime>; null on a homepage without article types (Article, BlogPosting, NewsArticle, TechArticle, HowTo, FAQPage); no date at all = not fresh",
    "securityHeadersMin": "presentHeaders >= securityHeadersMin",
    "securityHeadersTotal": "number of header names in securityHeaders",
    "maxRedirectHops": "every host variant chain (http/https x apex/www) has hops <= maxRedirectHops",
    "brokenLinksMax": "brokenCount <= brokenLinksMax, a link is broken when it answers >= 400 or errors",
    "maxBrokenLinkCandidates": "at most maxBrokenLinkCandidates same-host links are checked",
    "mixedContentMaxUrls": "at most mixedContentMaxUrls http:// resource URLs are reported; the check fails on the first one",
    "productSchemaMinPercent": "round(passingFields / productCompletenessChecks * 100) >= productSchemaMinPercent; null without a Product; SaaS products and Products next to Service/Organization pass at 100",
    "productCompletenessChecks": "fields counted: sku, gtin or gtin13 or mpn, description, category, brand as object, image as array, aggregateRating, review",
    "aiCrawlerMajorsBlockedMax": "blockedMajors <= aiCrawlerMajorsBlockedMax; a missing or empty robots.txt allows everything",
    "explicitPolicyMinKeyBots": "keyBotsWithOwnUserAgentBlock >= explicitPolicyMinKeyBots (a Disallow block counts as explicit too)",
    "sitemapFreshnessDays": "newest lastmod is younger than sitemapFreshnessDays",
    "robotsAllowsSearchEngines": "fails only when one of googlebot or bingbot is blocked from /; a bot with its own User-agent block ignores the wildcard block; a missing or empty robots.txt allows everything"
  },
  "partialCredit": 0.7,
  "metrics": [
    {
      "field": "has_valid_meta_attrs",
      "weight": 15,
      "tier": "high",
      "partialField": "meta_partial"
    },
    {
      "field": "has_valid_json_ld",
      "weight": 15,
      "tier": "high",
      "partialField": "json_ld_exists"
    },
    {
      "field": "robots_allows_search_engines",
      "weight": 15,
      "tier": "high"
    },
    {
      "field": "has_valid_title",
      "weight": 10,
      "tier": "medium"
    },
    {
      "field": "robots_allows_ai_crawlers",
      "weight": 10,
      "tier": "medium",
      "advancedOnly": true
    },
    {
      "field": "has_valid_sitemap",
      "weight": 10,
      "tier": "medium",
      "advancedOnly": true,
      "partialField": "sitemap_exists"
    },
    {
      "field": "has_valid_canonical",
      "weight": 10,
      "tier": "medium",
      "advancedOnly": true
    },
    {
      "field": "has_valid_heading_hierarchy",
      "weight": 10,
      "tier": "medium",
      "advancedOnly": true
    }
  ],
  "composites": [
    {
      "key": "content_quality",
      "weight": 5,
      "members": [
        "valuable_unique_content",
        "search_enginge_relevance",
        "llm_chatbot_relevant"
      ],
      "advancedOnly": false
    },
    {
      "key": "ai_citation_readiness",
      "weight": 8,
      "members": [
        "has_ai_links",
        "ai_files",
        "has_explicit_ai_bot_policy",
        "has_content_freshness",
        "has_author_attribution"
      ],
      "advancedOnly": true
    },
    {
      "key": "technical_detail",
      "weight": 5,
      "members": [
        "has_no_duplicate_meta",
        "has_valid_meta_lengths",
        "has_image_alt_coverage"
      ],
      "advancedOnly": true
    },
    {
      "key": "structured_data_depth",
      "weight": 5,
      "members": [
        "has_valid_jsonld_urls",
        "has_og_type_match",
        "has_sameas_consistency"
      ],
      "advancedOnly": true
    },
    {
      "key": "trust_infrastructure",
      "weight": 5,
      "members": [
        "https_enforced",
        "has_security_headers",
        "has_no_mixed_content",
        "has_clean_redirects",
        "sitemap_declared_in_robots",
        "has_no_render_blocking",
        "has_no_broken_links",
        "perf_rating"
      ],
      "advancedOnly": true
    }
  ],
  "scoring": {
    "compositePassRateSteps": {
      "full": 1,
      "partial": 0.5
    },
    "compositeSpecialCases": {
      "ai_files": {
        "partialPassWhen": "has_ai_llmstxt",
        "partialPassValue": 0.5
      },
      "perf_rating": {
        "full": "good",
        "partial": "needs-improvement",
        "partialPassValue": 0.5
      }
    }
  },
  "checkGroups": [
    {
      "key": "foundation",
      "label": "Foundation",
      "fields": [
        "has_valid_title",
        "has_valid_meta_attrs",
        "robots_allows_search_engines",
        "has_valid_canonical",
        "has_no_duplicate_meta",
        "has_valid_meta_lengths",
        "has_valid_heading_hierarchy",
        "has_image_alt_coverage",
        "has_no_render_blocking",
        "has_valid_meta_robots_attrs",
        "has_valid_og_meta_attrs"
      ],
      "checks": [
        {
          "field": "has_valid_title",
          "label": "Valid Title"
        },
        {
          "field": "has_valid_meta_attrs",
          "label": "Valid Meta Attributes"
        },
        {
          "field": "robots_allows_search_engines",
          "label": "Search Engine Access"
        },
        {
          "field": "has_valid_canonical",
          "label": "Valid Canonical"
        },
        {
          "field": "has_no_duplicate_meta",
          "label": "No Duplicate Meta"
        },
        {
          "field": "has_valid_meta_lengths",
          "label": "Valid Meta Lengths"
        },
        {
          "field": "has_valid_heading_hierarchy",
          "label": "Valid Heading Hierarchy"
        },
        {
          "field": "has_image_alt_coverage",
          "label": "Image Alt Coverage"
        },
        {
          "field": "has_no_render_blocking",
          "label": "No Render-Blocking Resources"
        },
        {
          "field": "has_valid_meta_robots_attrs",
          "label": "Valid Meta Robots Attributes"
        },
        {
          "field": "has_valid_og_meta_attrs",
          "label": "Valid Open Graph Attributes"
        }
      ]
    },
    {
      "key": "ai_readiness",
      "label": "AI Readiness",
      "fields": [
        "has_valid_json_ld",
        "ai_files",
        "robots_allows_ai_crawlers",
        "has_valid_jsonld_urls",
        "has_complete_product_schema",
        "has_og_type_match",
        "has_sameas_consistency",
        "has_author_attribution",
        "has_content_freshness",
        "has_explicit_ai_bot_policy"
      ],
      "checks": [
        {
          "field": "has_valid_json_ld",
          "label": "Valid JSON-LD"
        },
        {
          "field": "ai_files",
          "label": "AI Files Present"
        },
        {
          "field": "robots_allows_ai_crawlers",
          "label": "AI Crawler Access"
        },
        {
          "field": "has_valid_jsonld_urls",
          "label": "Valid JSON-LD URLs"
        },
        {
          "field": "has_complete_product_schema",
          "label": "Complete Product Schema"
        },
        {
          "field": "has_og_type_match",
          "label": "OG Type Match"
        },
        {
          "field": "has_sameas_consistency",
          "label": "sameAs Consistency"
        },
        {
          "field": "has_author_attribution",
          "label": "Author Attribution"
        },
        {
          "field": "has_content_freshness",
          "label": "Content Freshness"
        },
        {
          "field": "has_explicit_ai_bot_policy",
          "label": "Explicit AI Bot Policy"
        }
      ]
    },
    {
      "key": "content_quality",
      "label": "Content Quality",
      "fields": [
        "valuable_unique_content",
        "seo_optimized",
        "search_enginge_relevance",
        "llm_chatbot_relevant"
      ],
      "checks": [
        {
          "field": "valuable_unique_content",
          "label": "Valuable Unique Content"
        },
        {
          "field": "seo_optimized",
          "label": "SEO Optimized"
        },
        {
          "field": "search_enginge_relevance",
          "label": "Search Engine Relevance"
        },
        {
          "field": "llm_chatbot_relevant",
          "label": "LLM Chatbot Relevant"
        }
      ]
    }
  ],
  "majorAiBots": [
    "gptbot",
    "claudebot",
    "google-extended",
    "perplexitybot"
  ],
  "keyAiBots": [
    "gptbot",
    "claudebot",
    "google-extended",
    "perplexitybot",
    "applebot-extended"
  ],
  "majorSearchEngineBots": [
    "googlebot",
    "bingbot"
  ],
  "securityHeaders": [
    "strict-transport-security",
    "x-frame-options",
    "x-content-type-options",
    "referrer-policy",
    "permissions-policy",
    "content-security-policy"
  ]
}
