{
  "$id": "https://law.arxo.io/schema/review-approval.schema.json",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "additionalProperties": false,
  "description": "A lawyer's approval of a single block of a review document. The approval is pinned to a PAIR of hashes: the node semantics (the semantic-hash filter applied to the node) and the bytes of the verbalized block. A mismatch in either one moves the approval to stale (LDC-E8202): for semantics, because a different meaning was signed; for text, because different text was signed.",
  "$comment": "Approval workflow docs; SPEC §209",
  "properties": {
    "at": {
      "description": "Moment of approval. Data about a human action: it comes from outside, not from the verbalizer clock, which is a pure function. The `format` keyword of Draft 2020-12 is only an annotation, so the record shape is held by `pattern`, which matches the Instant type of the normative schemas.",
      "$comment": "SPEC §3.2; E-0060; SPEC §2.15",
      "format": "date-time",
      "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:Z|[+-]\\d{2}:\\d{2})$",
      "type": "string"
    },
    "kind": {
      "const": "review_approval"
    },
    "language": {
      "description": "Language tag of the text as read and approved.",
      "$comment": "SPEC §13",
      "minLength": 2,
      "type": "string"
    },
    "note": {
      "description": "Reviewer's annotation; does not affect the status.",
      "type": "string"
    },
    "programSemanticHash": {
      "$ref": "#/$defs/Digest",
      "description": "Semantic hash of the program within which the node was read. Provenance: by itself it does not cause staleness — otherwise editing a neighboring norm would stale-out every approval in the package.",
      "$comment": "SPEC §209"
    },
    "reviewer": {
      "description": "Stable identifier of the reviewer. Who signed is part of the record, not the log.",
      "$comment": "SPEC §14",
      "minLength": 1,
      "type": "string"
    },
    "schemaVersion": {
      "const": "law.review/0.1"
    },
    "status": {
      "description": "Aligned with the review workflow and coverage levels: only `approved` is a necessary condition for a fragment's transition to VERIFIED.",
      "$comment": "SPEC §33.1",
      "enum": ["draft", "reviewed", "verified", "approved", "disputed"]
    },
    "subjectId": {
      "description": "Stable identifier of the compiled (CLIR) node whose block was approved.",
      "$comment": "SPEC §14",
      "minLength": 1,
      "type": "string"
    },
    "subjectSemanticHash": {
      "$ref": "#/$defs/Digest",
      "description": "Semantic hash of the NODE: the node's canonical bytes after the semantic-hash filter (labels, annotations, sourceSpan, and derived hashes excluded). Editing a label does not change it — it changes only the verbalization hash.",
      "$comment": "SPEC §209; E-0010"
    },
    "templatePack": {
      "description": "Name and version of the template pack in the form `name@version` — text provenance.",
      "pattern": "^[a-z][a-z0-9_.]*@[0-9]+\\.[0-9]+\\.[0-9]+$",
      "type": "string"
    },
    "verbalizationHash": {
      "$ref": "#/$defs/Digest",
      "description": "sha256 of the verbalized block's bytes (not the whole document): approval of one norm must not go stale from editing a neighboring one."
    }
  },
  "required": [
    "schemaVersion",
    "kind",
    "subjectId",
    "subjectSemanticHash",
    "verbalizationHash",
    "templatePack",
    "language",
    "reviewer",
    "status",
    "at"
  ],
  "type": "object",
  "$defs": {
    "Digest": {
      "pattern": "^sha256:[0-9a-f]{64}$",
      "type": "string"
    }
  }
}
