{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://law.arxo.io/schema/source-mapping.schema.json",
  "title": "Arxo Fact Protocol source mapping 0.1",
  "description": "An authoring-time, pinnable declaration of how ONE external record — a database row, a bus event, a form submission, an API response, serialized as a JSON document — becomes facts of the Arxo Fact Protocol. It names the model and its semantic hash; the record scopes addressed by JSON Pointer (RFC 6901) with the entity each scope instance denotes; typed fields; fact entries; provenance; and an optional evidence document for the record itself. It carries no semantics: the mapping computes nothing — each field value becomes a typed model term of a fact with declared origin, and the record becomes the evidence item the facts point to. The output is a fact set. The contentHash self-pin excludes its own field. The tool checks scope and field names, pointers, predicates, and types against the compiled model, reporting mapping errors; the schema holds only the shape. Tabular microdata stay with the population binding.", "$comment": "SPEC §73; SPEC §74; SPEC §208; DECISION-0396; DECISION-0357 §6; DECISION-0360; DECISION-0264",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "schemaVersion",
    "id",
    "model",
    "scopes",
    "facts",
    "contentHash"
  ],
  "properties": {
    "schemaVersion": {
      "const": "law.source-mapping/0.1"
    },
    "id": {
      "description": "Mapping identifier; the extractor name of every emitted fact is source-mapping:<id>.",
      "type": "string",
      "pattern": "^[A-Za-z0-9_.:-]+$"
    },
    "description": {
      "description": "Free text for the reader: which system, table, topic or form the record comes from. Not read by the tool.",
      "type": "string"
    },
    "model": {
      "type": "object",
      "additionalProperties": false,
      "required": ["name", "version", "semanticHash"],
      "properties": {
        "name": {
          "description": "Model package name, as in an import.",
          "$ref": "population-binding.schema.json#/$defs/PackageName"
        },
        "version": {
          "$ref": "population-binding.schema.json#/$defs/Version"
        },
        "semanticHash": {
          "description": "The semantic hash of the model’s compiled form as given to the tool; a mismatch triggers SOURCE_MAPPING_MODEL_MISMATCH.",
          "$ref": "screen-binding.schema.json#/$defs/Digest"
        }
      }
    },
    "scopes": {
      "description": "Parts of the record that denote entities. Exactly one root scope (no parent); every other scope names its parent, and the parent chain reaches the root.",
      "type": "array",
      "minItems": 1,
      "items": {
        "$ref": "#/$defs/Scope"
      }
    },
    "facts": {
      "type": "array",
      "minItems": 1,
      "items": {
        "$ref": "#/$defs/Fact"
      }
    },
    "provenance": {
      "description": "Provenance shared by every emitted fact. origin defaults to case_input; extraction is not a new origin.", "$comment": "SPEC §73; DECISION-0360",
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "origin": {
          "enum": ["case_input", "source_asserted", "external_snapshot", "adjudicated", "assumed_for_simulation"]
        },
        "observedAt": {
          "description": "When the source observed the facts: a root-scope field of type Instant, or a constant Instant.",
          "$ref": "#/$defs/InstantSource"
        },
        "recordedAt": {
          "description": "When the source recorded them: a root-scope field of type Instant, or a constant Instant.",
          "$ref": "#/$defs/InstantSource"
        }
      }
    },
    "evidence": {
      "description": "The record as an evidence item: every emitted fact points to it, and its document hash is the sha256 of the exact record bytes. Without this section facts carry no document, and the core warns FACT_WITHOUT_EVIDENCE — the honest signal that the record itself was not presented.", "$comment": "SPEC §74; DECISION-0360",
      "type": "object",
      "additionalProperties": false,
      "required": ["idPattern", "type"],
      "properties": {
        "idPattern": {
          "description": "Evidence id with {pointer} substitutions from the root scope instance.",
          "$ref": "#/$defs/IdPattern"
        },
        "type": {
          "description": "evidenceType of the item; read only by the package's EvidencePolicy.",
          "$ref": "legal-ir.schema.json#/$defs/TypeRef"
        },
        "status": {
          "description": "Evidence status; defaults to recorded.",
          "type": "string",
          "minLength": 1
        },
        "payload": {
          "description": "Payload keys filled from root-scope fields, in the order given; an absent field leaves the key out.",
          "type": "array",
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": ["key", "field"],
            "properties": {
              "key": {
                "type": "string",
                "pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
              },
              "field": {
                "$ref": "#/$defs/Name"
              }
            }
          }
        }
      }
    },
    "contentHash": {
      "$ref": "screen-binding.schema.json#/$defs/Digest"
    }
  },
  "$defs": {
    "Name": {
      "type": "string",
      "pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
    },
    "Pointer": {
      "description": "JSON Pointer (RFC 6901). The empty string is the element itself.",
      "type": "string",
      "pattern": "^(/([^~/]|~[01])*)*$"
    },
    "IdPattern": {
      "description": "Text with {pointer} substitutions, where pointer is a JSON Pointer relative to the scope element ({/id}) or, prefixed with $, absolute from the record root ({$/tenant}). The substituted value must be a string or an integer; null or missing triggers RECORD_KEY_NULL. The result must be a URN or IRI.",
      "type": "string",
      "minLength": 1
    },
    "Scope": {
      "type": "object",
      "additionalProperties": false,
      "required": ["name", "pointer", "entity"],
      "properties": {
        "name": {
          "$ref": "#/$defs/Name"
        },
        "pointer": {
          "description": "Root scope: absolute pointer to an object (the record must have it, otherwise SOURCE_RECORD_INVALID). Child scope: pointer relative to the parent element.",
          "$ref": "#/$defs/Pointer"
        },
        "parent": {
          "$ref": "#/$defs/Name"
        },
        "each": {
          "description": "Child scope only. true: the pointer addresses an array, each element is one instance (null or missing is zero instances). false (default): the pointer addresses one object (null or missing is no instance).",
          "type": "boolean"
        },
        "entity": {
          "type": "object",
          "additionalProperties": false,
          "required": ["idPattern", "type"],
          "properties": {
            "idPattern": {
              "$ref": "#/$defs/IdPattern"
            },
            "type": {
              "description": "TypeRef of an entity type of the model.",
              "$ref": "legal-ir.schema.json#/$defs/TypeRef"
            }
          }
        },
        "fields": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/Field"
          }
        }
      }
    },
    "Field": {
      "type": "object",
      "additionalProperties": false,
      "required": ["name", "pointer", "type"],
      "properties": {
        "name": {
          "$ref": "#/$defs/Name"
        },
        "pointer": {
          "description": "Pointer relative to the scope element. JSON null or a missing member is absence of the value.",
          "$ref": "#/$defs/Pointer"
        },
        "type": {
          "description": "TypeRef: urn:law:std#Integer, Decimal, Money, Boolean, Text, Date, Instant, or an enum of the model.",
          "$ref": "legal-ir.schema.json#/$defs/TypeRef"
        },
        "currency": {
          "description": "Money only, exclusive with currencyPointer: ISO 4217 code of every value.",
          "type": "string",
          "pattern": "^[A-Z]{3}$"
        },
        "currencyPointer": {
          "description": "Money only, exclusive with currency: pointer relative to the scope element to a three-letter code.",
          "$ref": "#/$defs/Pointer"
        },
        "members": {
          "description": "Enum only: source value (a string, or the decimal text of an integer) to member name. Non-empty, every value a non-empty string: the tool checks it (SOURCE_MAPPING_INVALID), so every runner validates one keyword set.",
          "type": "object"
        },
        "trueValues": {
          "description": "Boolean only: strings read as true, in addition to JSON true.",
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "falseValues": {
          "description": "Boolean only: strings read as false, in addition to JSON false.",
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "nullValues": {
          "description": "Strings read as absence, in addition to JSON null (for instance an empty string of a form).",
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      }
    },
    "Fact": {
      "type": "object",
      "additionalProperties": false,
      "required": ["predicate", "scope", "arguments", "presence"],
      "properties": {
        "predicate": {
          "description": "symbol_decl id of a relation of the model (URN with #).",
          "type": "string",
          "minLength": 1
        },
        "scope": {
          "description": "Scope whose instances emit the fact: one fact per instance.",
          "$ref": "#/$defs/Name"
        },
        "arguments": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/Argument"
          }
        },
        "presence": {
          "oneOf": [
            {
              "type": "object",
              "additionalProperties": false,
              "required": ["kind"],
              "properties": {
                "kind": {
                  "const": "always"
                }
              }
            },
            {
              "description": "The fact is emitted only when the Boolean field is true; false or absent emits nothing.",
              "type": "object",
              "additionalProperties": false,
              "required": ["kind", "field"],
              "properties": {
                "kind": {
                  "const": "boolean_field"
                },
                "field": {
                  "$ref": "#/$defs/Name"
                }
              }
            }
          ]
        },
        "required": {
          "description": "An absent field argument refuses the whole record (RECORD_REQUIRED_NULL) instead of dropping the fact. Default false.",
          "type": "boolean"
        }
      }
    },
    "Argument": {
      "oneOf": [
        {
          "description": "The entity of this scope or of one of its ancestors.",
          "type": "object",
          "additionalProperties": false,
          "required": ["kind", "scope"],
          "properties": {
            "kind": {
              "const": "entity"
            },
            "scope": {
              "$ref": "#/$defs/Name"
            }
          }
        },
        {
          "description": "A field of the fact's scope.",
          "type": "object",
          "additionalProperties": false,
          "required": ["kind", "field"],
          "properties": {
            "kind": {
              "const": "field"
            },
            "field": {
              "$ref": "#/$defs/Name"
            }
          }
        },
        {
          "description": "A constant: value is read by the same rules as a field of that type; for an enum it is the member name.",
          "type": "object",
          "additionalProperties": false,
          "required": ["kind", "type", "value"],
          "properties": {
            "kind": {
              "const": "constant"
            },
            "type": {
              "$ref": "legal-ir.schema.json#/$defs/TypeRef"
            },
            "value": {
              "type": ["string", "integer", "boolean"]
            },
            "currency": {
              "type": "string",
              "pattern": "^[A-Z]{3}$"
            }
          }
        }
      ]
    },
    "InstantSource": {
      "oneOf": [
        {
          "type": "object",
          "additionalProperties": false,
          "required": ["field"],
          "properties": {
            "field": {
              "$ref": "#/$defs/Name"
            }
          }
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": ["constant"],
          "properties": {
            "constant": {
              "type": "string",
              "minLength": 1
            }
          }
        }
      ]
    }
  }
}
