{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://www.ai-visibility.org.uk/specifications/validator-output.schema.json",
  "title": "AI Discovery Files Validator Output",
  "description": "Standard output format for any conformant AI Discovery Files validator. Defines a deterministic, machine-readable result so different validators can produce equivalent output for the same target. Implementations MAY produce richer optional diagnostics, but the required fields MUST conform.",
  "type": "object",
  "required": [
    "$schema",
    "target",
    "validator",
    "checkedAt",
    "specVersion",
    "files",
    "contradictions",
    "conformance"
  ],
  "properties": {
    "$schema": {
      "type": "string",
      "format": "uri",
      "const": "https://www.ai-visibility.org.uk/specifications/validator-output.schema.json",
      "description": "Reference to this schema. MUST be present so consumers can identify the output format."
    },
    "target": {
      "type": "object",
      "description": "The site being validated.",
      "required": ["host"],
      "properties": {
        "host": {
          "type": "string",
          "minLength": 1,
          "description": "The hostname under validation (e.g. example.com). No protocol, no path."
        },
        "url": {
          "type": "string",
          "format": "uri",
          "description": "Canonical URL the validator resolved to (with protocol, including any www redirect)."
        }
      },
      "additionalProperties": false
    },
    "validator": {
      "type": "object",
      "description": "Identifies the validator producing this output.",
      "required": ["name", "version"],
      "properties": {
        "name": {
          "type": "string",
          "minLength": 1,
          "description": "Human-readable validator name (e.g. 'AI Visibility Checker')."
        },
        "version": {
          "type": "string",
          "description": "Validator version. SHOULD follow Semver 2.0.0 if possible."
        },
        "url": {
          "type": "string",
          "format": "uri",
          "description": "Validator project or vendor URL."
        },
        "reference": {
          "type": "boolean",
          "description": "True if this validator is the reference implementation."
        }
      },
      "additionalProperties": false
    },
    "checkedAt": {
      "type": "string",
      "format": "date-time",
      "description": "ISO 8601 timestamp when the validation completed."
    },
    "specVersion": {
      "type": "string",
      "pattern": "^\\d+\\.\\d+\\.\\d+$",
      "description": "Version of the AI Discovery Files definition the validator targeted (e.g. 1.5.0)."
    },
    "registryUrl": {
      "type": "string",
      "format": "uri",
      "description": "Optional URL of the registry.json the validator consulted."
    },
    "files": {
      "type": "array",
      "description": "Per-file results, one entry per ADF the validator attempted to fetch. The entries MUST be deterministic for the same target; ordering SHOULD follow the registry order.",
      "items": { "$ref": "#/$defs/fileResult" }
    },
    "contradictions": {
      "type": "array",
      "description": "Cross-file contradictions detected (e.g. identity.json business name disagrees with brand.txt). An empty array means none were detected.",
      "items": { "$ref": "#/$defs/contradiction" }
    },
    "conformance": {
      "type": "object",
      "description": "Conformance evaluation against each class. A validator MUST evaluate at least the Essential class.",
      "required": ["claimed", "achieved"],
      "properties": {
        "claimed": {
          "type": ["string", "null"],
          "description": "Conformance class the publisher self-declared, if known. Null when no claim is found.",
          "enum": ["essential", "recommended", "complete", null]
        },
        "achieved": {
          "type": ["string", "null"],
          "description": "Highest conformance class the target actually passes, as determined by the validator. Null when no class is achieved.",
          "enum": ["essential", "recommended", "complete", null]
        },
        "classes": {
          "type": "object",
          "description": "Per-class pass/fail breakdown.",
          "properties": {
            "essential": { "$ref": "#/$defs/conformanceClassResult" },
            "recommended": { "$ref": "#/$defs/conformanceClassResult" },
            "complete": { "$ref": "#/$defs/conformanceClassResult" }
          },
          "additionalProperties": false
        }
      },
      "additionalProperties": false
    },
    "diagnostics": {
      "type": "array",
      "description": "Optional human-readable diagnostics (warnings, suggestions, recommendations). Validators MAY emit these alongside the deterministic fields above. Consumers MUST NOT rely on diagnostic wording being stable across validators.",
      "items": { "$ref": "#/$defs/diagnostic" }
    }
  },
  "additionalProperties": false,
  "$defs": {
    "fileResult": {
      "type": "object",
      "required": ["id", "name", "found", "valid"],
      "properties": {
        "id": {
          "type": "string",
          "pattern": "^ADF-\\d{3}$",
          "description": "Stable file identifier (e.g. ADF-001 for llms.txt)."
        },
        "name": {
          "type": "string",
          "description": "Filename (e.g. llms.txt)."
        },
        "url": {
          "type": "string",
          "format": "uri",
          "description": "URL the validator fetched."
        },
        "httpStatus": {
          "type": "integer",
          "minimum": 0,
          "description": "HTTP status code returned by the target (0 if no response)."
        },
        "redirectedTo": {
          "type": ["string", "null"],
          "format": "uri",
          "description": "Final URL after redirects, if redirects were followed."
        },
        "contentType": {
          "type": ["string", "null"],
          "description": "Content-Type header value returned by the target."
        },
        "contentLength": {
          "type": ["integer", "null"],
          "minimum": 0,
          "description": "Size of the response body in bytes."
        },
        "found": {
          "type": "boolean",
          "description": "Whether the file was fetched successfully (HTTP 200 or successful follow of a 301/302)."
        },
        "valid": {
          "type": "boolean",
          "description": "Whether the file passes the per-spec validation rules. False when found is false; false when content is malformed."
        },
        "errors": {
          "type": "array",
          "description": "Validation errors that prevent the file from being valid. Empty when valid is true.",
          "items": { "$ref": "#/$defs/diagnostic" }
        },
        "warnings": {
          "type": "array",
          "description": "Non-fatal issues. Validators SHOULD report warnings even when valid is true.",
          "items": { "$ref": "#/$defs/diagnostic" }
        }
      },
      "additionalProperties": false
    },
    "contradiction": {
      "type": "object",
      "required": ["between", "field", "message"],
      "properties": {
        "between": {
          "type": "array",
          "minItems": 2,
          "items": { "type": "string" },
          "description": "The filenames of the files that disagree, in order of authority."
        },
        "field": {
          "type": "string",
          "description": "The field or claim that disagrees (e.g. 'business name', 'service description')."
        },
        "message": {
          "type": "string",
          "minLength": 1,
          "description": "One-line plain-English summary of the contradiction."
        },
        "severity": {
          "type": "string",
          "enum": ["error", "warning"],
          "description": "Error indicates the contradiction prevents conformance; warning indicates a quality issue.",
          "default": "warning"
        }
      },
      "additionalProperties": false
    },
    "conformanceClassResult": {
      "type": "object",
      "required": ["pass", "missing"],
      "properties": {
        "pass": {
          "type": "boolean",
          "description": "Whether the target passes this conformance class."
        },
        "missing": {
          "type": "array",
          "items": { "type": "string" },
          "description": "Filenames required for this class that are missing or invalid. Empty when pass is true."
        },
        "reasons": {
          "type": "array",
          "items": { "type": "string" },
          "description": "Optional human-readable reasons for the result."
        }
      },
      "additionalProperties": false
    },
    "diagnostic": {
      "type": "object",
      "required": ["code", "message"],
      "properties": {
        "code": {
          "type": "string",
          "minLength": 1,
          "description": "Stable machine-readable identifier (e.g. 'http-404', 'malformed-json', 'placeholder-content'). Implementations MAY define their own codes; consumers SHOULD treat unknown codes as opaque."
        },
        "message": {
          "type": "string",
          "minLength": 1,
          "description": "Human-readable description."
        },
        "severity": {
          "type": "string",
          "enum": ["info", "warning", "error"],
          "default": "warning"
        },
        "requirement": {
          "type": "string",
          "pattern": "^ADF-[A-Z]+-\\d{3}$",
          "description": "Optional pointer to the numbered requirement clause this diagnostic relates to (e.g. ADF-IDENT-001)."
        }
      },
      "additionalProperties": false
    }
  }
}
