from typing import Literal from pydantic import BaseModel, ConfigDict, Field, field_validator from app.presets import VALID_PRESETS, PresetName, _VALID_OVERRIDE_KEYS PillarName = Literal["off_site", "scrapability", "seo"] EffortLevel = Literal["low", "medium", "high"] # A curated OpenAPI example for the audit request body. The example is the # smallest legal payload (URL only) so agents can copy/paste it as a smoke # test without needing to understand presets, overrides, or scope toggles # on first contact. _AUDIT_REQUEST_EXAMPLE: dict = { "url": "https://example.com/", } # A second example showing the preset path so agents discover the preset # model from the schema without reading docs first. _AUDIT_REQUEST_PRESET_EXAMPLE: dict = { "url": "https://example.com/", "preset": "blog", } class AuditRequest(BaseModel): # OpenAPI examples + field-level descriptions so the public schema is # self-explanatory for agents. The model_config block provides the JSON # Schema ``examples`` array surfaced under AuditRequest in OpenAPI. model_config = ConfigDict( json_schema_extra={ "examples": [ _AUDIT_REQUEST_EXAMPLE, _AUDIT_REQUEST_PRESET_EXAMPLE, ], }, ) url: str = Field( description=( "Public HTTP(S) URL to audit. The scheme is added automatically if " "missing (defaults to https). Private, loopback, link-local, and " "reserved address ranges are rejected before the audit starts." ), examples=["https://example.com/"], ) # The three legacy booleans remain part of the public contract (existing # integrations depend on them) but the preset path is the recommended one. # Marking the fields deprecated surfaces this in generated client SDKs and # OpenAPI tooling without breaking callers. include_protocols: bool = Field( default=False, deprecated=True, description=( "Legacy scope toggle. When ``preset`` is set this field is " "ignored; when ``preset`` is None, this controls whether " "protocol/API surfaces (MCP, A2A, Agent Skills, WebMCP, API " "catalog) are scored. Prefer the preset-based path for new " "integrations; this field is preserved for backward compatibility " "and remains part of the public contract." ), ) include_account_auth: bool = Field( default=False, deprecated=True, description=( "Legacy scope toggle. When ``preset`` is set this field is " "ignored; when ``preset`` is None, this controls whether " "account/auth surfaces (OAuth/OIDC discovery, auth.md) are " "scored. Prefer the preset-based path for new integrations; this " "field is preserved for backward compatibility and remains part " "of the public contract." ), ) include_ecommerce: bool = Field( default=False, deprecated=True, description=( "Legacy scope toggle. When ``preset`` is set this field is " "ignored; when ``preset`` is None, this controls whether " "commerce surfaces (Product/Offer JSON-LD, price/availability, " "checkout-protocol metadata) are scored. Prefer the preset-based " "path for new integrations; this field is preserved for backward " "compatibility and remains part of the public contract." ), ) preset: PresetName | None = Field( default=None, description=( "Preset identifier. When set, the preset wins over the " "three legacy include_* booleans. When None, the booleans are " "honored verbatim and custom_overrides must be empty. Supported " "values: 'blog', 'corporate', 'services', 'ecommerce', 'news', " "'saas', 'custom'." ), examples=["blog", "saas", None], ) custom_overrides: dict[str, bool] | None = Field( default=None, description=( "Custom/Power User overrides applied on top of the " "selected preset. Requires preset='custom' or another preset to " "be set; rejected when preset is None. Supported keys: " "'protocols', 'account_auth', 'ecommerce', 'feed_discovery', " "'article_schema', 'localbusiness_schema', 'news_article_schema', " "'claimreview_schema', 'product_offer_schema', 'commerce_fields', " "'api_catalog', 'mcp', 'a2a', 'agent_skills', 'webmcp', " "'oauth_oidc', 'ard_catalog', 'auth_md'." ), examples=[None, {"feed_discovery": True, "api_catalog": True}], ) @field_validator("preset") @classmethod def _validate_preset(_cls, value: str | None) -> str | None: if value is None: return value if value not in VALID_PRESETS: raise ValueError( f"Unknown preset {value!r}; valid presets: {sorted(VALID_PRESETS)}" ) return value @field_validator("custom_overrides") @classmethod def _validate_override_keys( _cls, value: dict[str, bool] | None ) -> dict[str, bool] | None: if value is None: return value unknown = sorted(set(value) - _VALID_OVERRIDE_KEYS) if unknown: raise ValueError( f"Unknown custom_overrides keys: {unknown}; " f"valid keys: {sorted(_VALID_OVERRIDE_KEYS)}" ) return value class CheckResult(BaseModel): """One row of the public audit report. A row corresponds to a check group (for example ``robots_txt`` or ``schema_ld``) that may bundle several underlying sub-signals. Rows that are not yet scored are returned with ``state='warn'`` and ``evidence_level='unknown'`` rather than being omitted from the response. """ pillar: PillarName = Field( description=( "Pillar this row contributes to. ``off_site`` covers off-site " "presence and entity metadata; ``scrapability`` covers AI " "crawler access, structured data, and machine-readable surfaces; " "``seo`` covers traditional search discovery signals." ), examples=["scrapability"], ) check_name: str = Field( description=( "Stable machine identifier for the check group. Use this key " "rather than ``label`` for downstream filtering; ``label`` is " "human-readable and may change without a contract bump." ), examples=["robots_txt", "schema_ld", "llms_txt"], ) label: str = Field( description=( "Human-readable title for the check group, suitable for " "displaying in dashboards." ), examples=["AI Bot Policy Signals"], ) state: Literal["pass", "partial", "fail", "warn", "locked"] = Field( description=( "Outcome state. ``pass`` and ``partial`` mean the row scored " "evidence on the live site; ``fail`` means the row ran and the " "site did not satisfy it; ``warn`` means the row is inconclusive " "(check failed to complete or returned no signal); ``locked`` " "means the row is reserved for a paid tier and is not scored in " "Essentials." ), examples=["partial"], ) evidence_level: Literal[ "verified", "inferred", "unknown", "not_applicable" ] = Field( default="inferred", description=( "How strong the evidence behind this row is. ``verified`` means " "the live public response was inspected; ``inferred`` means the " "row used deterministic heuristics or metadata; ``unknown`` " "means the row could not be evaluated; ``not_applicable`` means " "the row was excluded by the active scope/preset. Default is " "``inferred``: most check results combine deterministic " "heuristics with snapshot evidence rather than a full verified " "live-response crawl, so the safer default avoids overclaiming " "verification when callers omit the field." ), examples=["inferred"], ) available_in: str = Field( default="Essentials", description=( "Tier or surface where this row is fully evaluated. ``Essentials`` " "means the row is scored in the free audit; ``Starter`` and " "``Pro`` mean the row is locked advanced coverage and not scored " "in Essentials." ), examples=["Essentials", "Starter", "Pro"], ) score: int = Field( description=( "Points earned on this row. Sum of all included row scores is " "the per-pillar score; overall score is the sum across the three " "pillars, capped at the rubric (100 points total)." ), examples=[3], ) max_score: int = Field( description=( "Maximum points this row can earn when the active scope includes " "every surface the row checks. The checked denominator across all " "Essentials rows is 56 by default and grows when scope toggles " "add protocol, account/auth, or commerce surfaces." ), examples=[6], ) finding: str = Field( description=( "Public-facing explanation of what was observed. Safe to surface " "directly in dashboards; never contains internal infrastructure " "detail or fetched response bodies." ), examples=["The robots.txt file does not mention the GPTBot user-agent."], ) fix: str = Field( description=( "Public-facing remediation hint tied to this finding. The hint is " "deterministic and never presumes access to internal tooling." ), examples=[ "Add an explicit GPTBot directive (Allow or Disallow) to robots.txt." ], ) effort: EffortLevel = Field( description=( "Estimated effort to apply the suggested fix. ``low`` is a " "single-file config edit; ``medium`` is a small code or template " "change; ``high`` is a structural site change." ), examples=["low"], ) class AuditScope(BaseModel): include_protocols: bool = Field( description=( "Resolved protocol/API scope for this audit. ``True`` when " "protocol families (API catalog, MCP, A2A, Agent Skills, WebMCP) " "are scored; derived from the preset when one is applied." ), ) include_account_auth: bool = Field( description=( "Resolved account/auth scope. ``True`` when OAuth/OIDC or " "auth.md surfaces are scored." ), ) include_ecommerce: bool = Field( description=( "Resolved commerce scope. ``True`` when Product/Offer JSON-LD, " "price/availability, and commerce-protocol metadata are scored." ), ) label: str = Field( description=( "Human-readable label for the resolved scope, suitable for " "displaying next to the score. For preset audits this is the " "preset label (for example ``'Blog/Content audit'``); for legacy " "boolean audits this is the legacy ``General website + extras`` " "string." ), examples=["Blog/Content audit"], ) included_optional_surfaces: list[str] = Field( description=( "Optional surfaces (protocol, account/auth, commerce) that were " "enabled for this audit. Empty when only the universal core is " "scored." ), ) excluded_optional_surfaces: list[str] = Field( description=( "Optional surfaces that were deliberately excluded by the " "preset/legacy booleans. The audit never penalises a site for " "excluded surfaces." ), ) # Preset resolution output (added fields). preset_applied: str | None = Field( default=None, description=( "Preset identifier that won scope resolution for this " "audit, or ``null`` when the legacy boolean path was used." ), examples=[None, "blog", "saas"], ) overrides_applied: dict[str, bool] = Field( default_factory=dict, description=( "Custom/Power User override keys that the audit actually applied " "on top of the preset defaults. Empty for legacy audits." ), ) included_families: list[str] = Field( default_factory=list, description=( "Sub-signal families that were enabled for this audit after " "preset + override resolution. Universal-core families are not " "listed here." ), ) excluded_families: list[str] = Field( default_factory=list, description=( "Sub-signal families that were explicitly disabled for this " "audit (by preset, override, or universal exclusion)." ), ) machine_surfaces_scope: Literal["common-contextual", "full"] = Field( default="common-contextual", description=( "Agent Protocol Discovery depth. ``common-contextual`` is the " "default bounded probe; ``full`` is selected by presets that " "explicitly opt into the complete protocol surface set." ), examples=["common-contextual", "full"], ) class PillarScores(BaseModel): off_site: int = Field( description="Points earned on the off-site pillar (0-30).", ) scrapability: int = Field( description="Points earned on the AI access / scrapability pillar (0-40).", ) seo: int = Field( description="Points earned on the SEO pillar (0-30).", ) class PillarMax(BaseModel): off_site: int = Field( description="Maximum off-site pillar score possible under the resolved scope.", ) scrapability: int = Field( description="Maximum AI access pillar score possible under the resolved scope.", ) seo: int = Field( description="Maximum SEO pillar score possible under the resolved scope.", ) class BenchmarkEntry(BaseModel): name: str = Field(description="Display name for the benchmark peer.") category: str = Field( description=( "Peer category bucket used by the benchmark profile generator " "(for example, ``'blog'``, ``'saas'``, ``'ecommerce'``)." ), ) group: str = Field( description=( "Peer size/cohort group (``'small'``, ``'medium'``, ``'large'``) " "used for percentile grouping." ), ) size: str = Field( description="Human-readable size label for the peer.", ) url: str = Field( description=( "Public benchmark peer URL. Safe to surface in dashboards; no " "private benchmarks are exposed through this field." ), ) overall_score: int = Field(description="Peer overall score at snapshot time.") free_evidence_score: int = Field( description="Peer free-evidence score at snapshot time." ) checked_score: int = Field(description="Peer checked-score at snapshot time.") checked_max: int = Field(description="Peer checked denominator at snapshot time.") agent_readiness_score: int = Field( description="Peer strict agent-readiness score at snapshot time." ) agent_readiness_earned: int = Field( description="Peer strict agent-readiness earned points." ) agent_readiness_max: int = Field( description="Peer strict agent-readiness denominator." ) pillar_scores: PillarScores = Field( description="Peer pillar scores at snapshot time." ) class BenchmarkComparison(BaseModel): score: int = Field( description=( "The site's free-evidence score (Essentials evidence only, no " "locked rows). This is the score used for peer comparison." ), ) checked_score: int = Field( description="Checked score earned by the site on this audit." ) checked_max: int = Field( description="Checked denominator for the resolved scope of this audit." ) benchmark_count: int = Field( description="Number of public benchmark peers considered." ) median_score: int = Field( description="Median free-evidence score across public benchmark peers." ) percentile: int = Field( description=( "Site's percentile rank against public peers (0-100). 100 means " "the site scored at or above every peer; 0 means it scored at or " "below every peer." ), ) position_label: str = Field( description=( "Human-readable position label (for example, ``'Above median'`` " "or ``'Below median'``). Always paired with the numeric " "percentile; never the only signal." ), examples=["Above median", "At median", "Below median"], ) nearest: list[BenchmarkEntry] = Field( description=( "Closest peers by free-evidence score, used for explainable " "comparison. Empty when no peers are available." ), ) entries: list[BenchmarkEntry] = Field( description=( "All public peers considered for this comparison. Empty when no " "public profiles are loaded; private production profiles are " "never exposed through this field." ), ) basis: str = Field( description=( "Plain-text description of how the benchmark was computed " "(profile source, denominator, scope filter). Always includes " "the public/private source distinction." ), ) snapshot_date: str = Field( description=( "ISO date the benchmark snapshot was generated. Benchmark " "profiles are refreshed when scoring, options, or applicability " "rules change." ), examples=["2026-07-17"], ) caveat: str = Field( description=( "Mandatory caveat explaining what the benchmark does and does " "not prove. Agents should surface this text alongside any " "percentile or comparison claim." ), ) class AgentBenchmarkComparison(BaseModel): score: int = Field(description="Site's strict agent-readiness score.") earned: int = Field(description="Points earned on the strict agent-readiness lens.") max: int = Field( description="Strict agent-readiness denominator for the resolved scope." ) benchmark_count: int = Field( description="Number of public peers considered for the agent benchmark." ) median_score: int = Field( description="Median strict agent-readiness score across public peers." ) percentile: int = Field( description="Site's percentile rank on the strict agent-readiness lens." ) position_label: str = Field( description="Human-readable agent-benchmark position label." ) nearest: list[BenchmarkEntry] = Field( description="Closest peers by strict agent-readiness score." ) entries: list[BenchmarkEntry] = Field( description="All public peers considered for the agent benchmark." ) basis: str = Field( description="Plain-text basis string for the agent benchmark." ) snapshot_date: str = Field( description="ISO date the agent-benchmark snapshot was generated." ) caveat: str = Field( description="Mandatory caveat explaining what the agent benchmark does and does not prove." ) class AgentReadinessCategory(BaseModel): name: str = Field(description="Category name (for example, ``'Discovery'``).") earned: int = Field(description="Points earned in this category.") max: int = Field(description="Maximum points for this category.") score: int = Field(description="Category percentage score (0-100).") passed: list[str] = Field( description="Public signals that this category verified on the site." ) missing: list[str] = Field( description="Public signals this category looked for and did not find." ) excluded: list[str] = Field( description=( "Surfaces excluded by the active scope/preset. Listed for " "transparency; they do not affect the category score." ), ) class AgentReadinessSummary(BaseModel): score: int = Field( description=( "Strict agent-readiness percentage score (0-100). Measures " "explicit agent-native discovery and protocol signals; stricter " "than general crawlability or SEO." ), ) earned: int = Field( description="Points earned on the strict agent-readiness lens." ) max: int = Field( description="Strict agent-readiness denominator for the resolved scope." ) label: str = Field( description="Human-readable label for the strict agent-readiness score.", examples=[ "Strong agent-native readiness", "Developing agent-native readiness", "Limited agent-native readiness", "Agent-native unavailable", ], ) categories: list[AgentReadinessCategory] = Field( description="Per-category breakdown of the strict agent-readiness score." ) passed: list[str] = Field( description="All agent-native signals verified on the site." ) missing: list[str] = Field( description="All agent-native signals the audit looked for and did not find." ) not_checked: list[str] = Field( description=( "Agent-native surfaces the audit deliberately did not check, " "for example when an individual probe failed and produced an " "inconclusive warning instead of verified evidence." ), ) benchmark: AgentBenchmarkComparison = Field( description="Peer comparison on the strict agent-readiness lens." ) caveat: str = Field( description=( "Mandatory caveat explaining what the strict agent-readiness " "score does and does not prove." ), ) class AuditResult(BaseModel): # API versioning. Additive only — the field carries the contract # version that produced this response. Clients may rely on it to detect # future additions without breaking on them. api_version: str = Field( default="1.0", description=( "MachineRead API contract version that produced this response. " "Add-only: the field value is bumped when the contract changes " "in a breaking way. Agents should treat unknown api_version " "values as forward-compatible and ignore unknown fields rather " "than reject the response." ), examples=["1.0"], ) url: str = Field( description=( "Final audited URL after normalization. Always starts with " "``https://`` (or ``http://`` when the user explicitly opted " "into plain HTTP) and ends with a trailing slash." ), ) scope: AuditScope = Field( description="Resolved audit scope (see AuditScope).", ) overall_score: int = Field( description=( "Full-rubric score on a 100-point scale. Includes locked " "advanced rows (score 0 until verified) and capped at the " "active pillar maxima." ), ) pillar_scores: PillarScores = Field( description="Per-pillar earned points." ) pillar_max: PillarMax = Field( description="Per-pillar maximum under the resolved scope." ) agent_readiness: AgentReadinessSummary = Field( description=( "Strict agent-native readiness summary. Stricter than the " "general scrapability pillar; measures explicit agent-protocol " "discovery and signal verification." ), ) benchmark: BenchmarkComparison = Field( description=( "Public peer comparison on the Essentials evidence score. " "Private production benchmark profiles are never exposed; only " "public-safe snapshot entries are returned." ), ) checks: list[CheckResult] = Field( description=( "Per-check group results. Length is 13 for a default Essentials " "audit; locked advanced rows are appended at the bottom with " "``state='locked'``." ), ) # Explicit public error models. These shapes are referenced in the # `responses={...}` mapping on `/v1/audit` so the autogenerated OpenAPI schema # documents the exact contract for agents. class ErrorMessage(BaseModel): detail: str = Field( description="Human-readable explanation of why the request was rejected.", examples=[ "URL resolves to a private address. Retry with a public HTTP(S) endpoint." ], ) class ValidationErrorMessage(BaseModel): detail: str = Field( description="Human-readable explanation of the validation failure.", examples=[ "Request body failed validation: url is required.", ], ) errors: list[dict] = Field( default_factory=list, description=( "Structured per-field or per-rule error descriptors. FastAPI's " "default 422 response uses a list of `{loc, msg, type}` items; " "preset override failures expose the offending keys and reasons." ), examples=[[]], ) class RateLimitErrorMessage(BaseModel): detail: str = Field( description="Human-readable explanation that the request was rate-limited.", examples=[ "Rate limit exceeded. Retry after the time indicated by Retry-After.", ], ) retry_after: int = Field( description=( "Whole seconds the client should wait before retrying. Mirrors " "the `Retry-After` response header and the `X-RateLimit-Reset` " "value." ), examples=[42], ) # Agent-oriented compact report summary. ``AuditSummary`` is the # strict-mode projection of ``AuditResult`` returned by # ``POST /v1/audit/summary``. It excludes LLM prose, finding/fix strings, # benchmark peer entries, agent-readiness passed/missing/not_checked lists, # rate-limit body metadata, and raw context. _SUMMARY_CHECK_NAME = Literal[ "robots_txt", "bot_access", "html_structure", "schema_ld", "llms_txt", "ssr", "machine_surfaces", "pagespeed", "canonical", "indexing", "search_discovery", "social", "wikipedia", ] _SUMMARY_PILLAR = Literal["off_site", "scrapability", "seo"] _SUMMARY_STATE = Literal["fail", "partial", "warn"] _SUMMARY_EVIDENCE = Literal["verified", "inferred", "sampled", "unavailable"] _SUMMARY_EFFORT = Literal["low", "medium", "high"] class SummaryScope(BaseModel): model_config = ConfigDict(extra="forbid", strict=True) preset: Literal["blog", "corporate", "services", "ecommerce", "news", "saas", "custom"] | None = Field( description=( "Preset identifier that won scope resolution for this audit, or " "``null`` when the legacy boolean path was used." ), examples=[None, "blog", "saas"], ) protocols: bool = Field( description=( "Resolved protocol/API scope for the audit. ``True`` when the " "active scope scores protocol-family surfaces." ), ) account_auth: bool = Field( description=( "Resolved account/auth scope for the audit. ``True`` when the " "active scope scores account/auth surfaces." ), ) ecommerce: bool = Field( description=( "Resolved commerce scope for the audit. ``True`` when the active " "scope scores commerce surfaces." ), ) overrides: dict[str, bool] = Field( default_factory=dict, description=( "Custom/Power User override keys the audit applied on top of the " "preset defaults. Empty when the legacy boolean path was used." ), ) class SummaryScorePair(BaseModel): model_config = ConfigDict(extra="forbid", strict=True) earned: int = Field(description="Points earned on the associated scope.") max: int = Field(description="Maximum points possible on the associated scope.") class SummaryPercent(BaseModel): model_config = ConfigDict(extra="forbid", strict=True) percent: int = Field(description="Percentage score for the associated scope (0-100).") earned: int = Field(description="Points earned on the associated scope.") max: int = Field(description="Maximum points possible on the associated scope.") class SummaryScores(BaseModel): model_config = ConfigDict(extra="forbid", strict=True) overall: SummaryScorePair = Field( description=( "Full-rubric overall score. ``max`` is the sum of pillar caps " "(100) regardless of the active scope." ), ) pillars: dict[str, SummaryScorePair] = Field( description=( "Per-pillar earned and max points. Keys are always " "``off_site``, ``scrapability``, and ``seo``." ), ) essentials: SummaryPercent = Field( description=( "Essentials evidence score (peer comparison source). ``percent`` " "mirrors ``AuditResult.benchmark.score``; ``earned`` and ``max`` " "are the source ``checked_score`` and ``checked_max``." ), ) agent_readiness: SummaryPercent = Field( description=( "Strict agent-readiness score. ``percent`` mirrors " "``AuditResult.agent_readiness.score``; ``earned`` and ``max`` " "are the source ``earned`` and ``max``." ), ) class SummaryBenchmark(BaseModel): model_config = ConfigDict(extra="forbid", strict=True) percentile: int = Field(description="Site percentile rank against public peers (0-100).") median_percent: int = Field( description=( "Median peer score (percent) the benchmark comparison used. The " "source field ``BenchmarkComparison.median_score`` is renamed to " "``median_percent`` to make the unit explicit in the agent surface." ), ) peer_count: int = Field(description="Number of public benchmark peers considered.") snapshot: str = Field( description=( "ISO date the benchmark snapshot was generated. Benchmark profiles " "are refreshed when scoring, options, or applicability rules change." ), ) class SummaryCheckCounts(BaseModel): model_config = ConfigDict(extra="forbid", populate_by_name=True) included: int = Field(description="Count of non-locked Essentials check rows (13).") locked: int = Field(description="Count of locked advanced rows (9).") # ``pass`` is a reserved keyword in Python. Pydantic v2 allows aliases # but we want the public JSON key to remain ``pass`` (matches the # runtime terminology). ``populate_by_name`` lets the constructor use # ``pass_count`` while the serialized JSON key stays ``pass``. pass_count: int = Field( alias="pass", description="Number of included rows with state ``pass``.", ) partial: int = Field(description="Number of included rows with state ``partial``.") fail: int = Field(description="Number of included rows with state ``fail``.") warn: int = Field(description="Number of included rows with state ``warn``.") attention_total: int = Field( description=( "Count of items in the ``attention`` array after the 5-row cap. " "This matches the actual returned count rather than the pre-cap " "candidate count so the field never disagrees with the array length." ), ) class SummaryAttentionItem(BaseModel): model_config = ConfigDict(extra="forbid", strict=True) check_name: _SUMMARY_CHECK_NAME = Field( description="Stable machine identifier for the check group.", ) pillar: _SUMMARY_PILLAR = Field(description="Pillar the row contributes to.") state: _SUMMARY_STATE = Field(description="Attention state of the row.") evidence_level: _SUMMARY_EVIDENCE = Field( description="Evidence maturity for the row.", ) earned: int = Field(description="Points earned on the row.") max: int = Field(description="Maximum points possible on the row.") effort: _SUMMARY_EFFORT = Field(description="Estimated remediation effort.") # Fixed ordered tuple of limitation codes that always appear in the # 200 payload in the same order. Exposed as a module-level constant so # tests, projection code, and the OpenAPI examples all reference the same # source of truth. SUMMARY_LIMITATION_CODES: tuple[ Literal["relative_scores"], Literal["no_live_ranking"], Literal["no_provider_ip_auth"], Literal["no_paid_crawlers"], ] = ( "relative_scores", "no_live_ranking", "no_provider_ip_auth", "no_paid_crawlers", ) class AuditSummary(BaseModel): # The agent-oriented compact report summary. The shape is # strict, additive-only, and intentionally smaller than # ``AuditResult`` so agents can reason about Essentials without # pulling the full per-check evidence into context. model_config = ConfigDict(extra="forbid", strict=True) api_version: Literal["1.0"] = Field( description=( "MachineRead API contract version that produced this response. " "Same semantics as ``AuditResult.api_version``." ), ) summary_version: Literal["1.0"] = Field( description=( "Summary contract version. Bumped when ``AuditSummary`` adds or " "renames a field in a breaking way." ), ) url: str = Field(description="Final audited URL after normalization.") scope: SummaryScope = Field(description="Resolved audit scope (compact form).") scores: SummaryScores = Field(description="Compact scoring surface.") benchmarks: dict[str, SummaryBenchmark] = Field( description=( "Compact benchmark comparison. Always carries the keys " "``essentials`` and ``agent_readiness``." ), ) checks: SummaryCheckCounts = Field(description="Compact check row counts.") attention: list[SummaryAttentionItem] = Field( description=( "Up to 5 most-urgent attention rows, sorted by severity then " "ratio then check name. Empty when every included row passes." ), ) limitations: tuple[str, str, str, str] = Field( description=( "Fixed ordered tuple of limitation codes that always appear in " "the 200 payload: ``relative_scores``, ``no_live_ranking``, " "``no_provider_ip_auth``, ``no_paid_crawlers``." ), )