{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://yarramate.org/schema/evidence/v1",
  "title": "YarraMate evidence overlay",
  "type": "object",
  "additionalProperties": false,
  "required": ["format", "id", "version", "provider", "observations"],
  "properties": {
    "format": {
      "const": "yarramate/evidence/v1"
    },
    "id": {
      "$ref": "#/$defs/id"
    },
    "version": {
      "type": "string",
      "pattern": "^[0-9]+\\.[0-9]+$"
    },
    "provider": {
      "$ref": "#/$defs/id"
    },
    "observations": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/observation"
      }
    }
  },
  "$defs": {
    "searchProbe": {
      "description": "One search a provider performed and found nothing at. Recorded so a reader can re-run it; yarramate does not execute it.",
      "type": "object",
      "additionalProperties": false,
      "oneOf": [{ "required": ["glob"] }, { "required": ["grep"] }],
      "properties": {
        "glob": {
          "type": "string",
          "minLength": 1,
          "description": "A path glob that matched nothing, e.g. PRAEFECT_*"
        },
        "grep": {
          "type": "string",
          "minLength": 1,
          "description": "A pattern that matched nothing."
        },
        "paths": {
          "type": "array",
          "minItems": 1,
          "items": { "type": "string", "minLength": 1 },
          "description": "Paths the search covered. Absent means the whole tree."
        }
      }
    },
    "measurement": {
      "description": "A figure quoted in an evidence message, with how it was produced. Recorded so a reader can re-derive it rather than trust it; yarramate does not execute the method.",
      "type": "object",
      "additionalProperties": false,
      "required": ["value", "method"],
      "properties": {
        "value": { "type": "string", "minLength": 1 },
        "method": {
          "type": "string",
          "minLength": 1,
          "description": "How the value was obtained, precisely enough to reproduce."
        }
      }
    },
    "id": {
      "type": "string",
      "pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"
    },
    "qualifiedIdentity": {
      "type": "string",
      "pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*(?:~[a-z][a-z0-9-]*)?$"
    },
    "evidenceLocator": {
      "type": "object",
      "additionalProperties": false,
      "required": ["uri"],
      "properties": {
        "uri": {
          "type": "string",
          "minLength": 1
        },
        "message": {
          "type": "string",
          "minLength": 1
        }
      }
    },
    "observationKey": {
      "type": "string",
      "pattern": "^[A-Za-z0-9][A-Za-z0-9._/-]*$"
    },
    "observation": {
      "type": "object",
      "additionalProperties": false,
      "required": ["result", "evidence"],
      "dependentRequired": {
        "key": ["value"],
        "value": ["key"]
      },
      "properties": {
        "subject": {
          "$ref": "#/$defs/qualifiedIdentity"
        },
        "claim": {
          "$ref": "#/$defs/qualifiedIdentity"
        },
        "result": {
          "enum": [
            "confirmed",
            "contradicted",
            "unknown",
            "not-observed"
          ]
        },
        "searched": {
          "type": "array",
          "minItems": 1,
          "items": { "$ref": "#/$defs/searchProbe" },
          "description": "What was searched and found empty. Meaningful on a not-observed result, which is the one result that asserts a negative about a tree nobody read exhaustively."
        },
        "measured": {
          "type": "array",
          "minItems": 1,
          "items": { "$ref": "#/$defs/measurement" },
          "description": "Figures quoted in the message, with how each was produced."
        },
        "key": {
          "$ref": "#/$defs/observationKey"
        },
        "value": {
          "type": "string",
          "minLength": 1
        },
        "evidence": {
          "$ref": "#/$defs/evidenceLocator"
        }
      },
      "oneOf": [
        {
          "required": ["subject"],
          "not": {
            "required": ["claim"]
          }
        },
        {
          "required": ["claim"],
          "not": {
            "required": ["subject"]
          }
        }
      ]
    }
  }
}
