{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://ooligo.io/schemas/clause-extraction/v1.json",
  "title": "Clause extraction record",
  "description": "Output of the clause-extraction skill, one record per contract. Replace this template with your firm's pinned schema. Bump $id on every breaking change so downstream consumers can key on version.",
  "type": "object",
  "required": ["contract_file", "contract_type", "extracted_at", "extractor_version", "clauses", "errors"],
  "additionalProperties": false,
  "properties": {
    "contract_file": { "type": "string", "minLength": 1 },
    "contract_type": { "type": "string", "enum": ["msa", "sow", "nda", "dpa", "order_form"] },
    "extracted_at": { "type": "string", "format": "date-time" },
    "extractor_version": { "type": "string", "pattern": "^clause-extraction@[0-9]{4}\\.[0-9]{2}$" },
    "source_metadata": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "page_count": { "type": "integer", "minimum": 1 },
        "heading_density": { "type": "string", "enum": ["high", "medium", "low"] },
        "ocr_layer_present": { "type": "boolean" }
      }
    },
    "clauses": {
      "type": "object",
      "description": "One property per clause_id from the taxonomy. Use $defs/clauseRecord for each.",
      "additionalProperties": { "$ref": "#/$defs/clauseRecord" }
    },
    "errors": {
      "type": "array",
      "items": {
        "type": "object",
        "required": ["code", "message"],
        "properties": {
          "code": { "type": "string", "enum": ["ocr_required", "schema_validation_failed", "endpoint_not_allowed", "excerpt_not_grounded", "taxonomy_mismatch"] },
          "message": { "type": "string" },
          "clause_id": { "type": "string" }
        }
      }
    }
  },
  "$defs": {
    "clauseRecord": {
      "type": "object",
      "required": ["status"],
      "additionalProperties": true,
      "properties": {
        "status": { "type": "string", "enum": ["extracted", "not_present", "error"] },
        "value": {
          "description": "Type depends on the clause's value_type in the taxonomy. Null when status != extracted.",
          "anyOf": [
            { "type": "string" },
            { "type": "number" },
            { "type": "boolean" },
            { "type": "object" },
            { "type": "null" }
          ]
        },
        "excerpt": {
          "type": "string",
          "maxLength": 280,
          "description": "Verbatim substring of the source. MUST be byte-identical to text in the source paragraphs — the hallucination guard rejects any excerpt not literally present."
        },
        "citation": {
          "type": "object",
          "required": ["page", "char_span"],
          "additionalProperties": false,
          "properties": {
            "page": { "type": "integer", "minimum": 1 },
            "char_span": {
              "type": "array",
              "minItems": 2,
              "maxItems": 2,
              "items": { "type": "integer", "minimum": 0 },
              "description": "[start_char_offset, end_char_offset] within the page's extracted text."
            }
          }
        },
        "confidence": { "type": "string", "enum": ["high", "medium", "low"] },
        "note": { "type": "string" },
        "error": { "type": "string" }
      },
      "allOf": [
        {
          "if": { "properties": { "status": { "const": "extracted" } } },
          "then": { "required": ["value", "excerpt", "citation", "confidence"] }
        },
        {
          "if": { "properties": { "status": { "const": "not_present" } } },
          "then": { "required": ["note"] }
        },
        {
          "if": { "properties": { "status": { "const": "error" } } },
          "then": { "required": ["error"] }
        }
      ]
    }
  }
}
