{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "motion.schema.json",
  "title": "Per-clip human-motion record (public delivery)",
  "description": "Human-motion record contract. One JSON record per clip; per-frame pose arrays (2D whole-body keypoints, 3D body model) travel as npz sidecars named in `files`, aligned so that row i of every array is frame i of the delivered video file. Release-wide facts (schema history, models and their licences, label rules) live once in the delivery's RELEASE.json, not in every record.",
  "type": "object",
  "required": ["schema", "release", "clip", "media", "content", "rights", "files", "quality", "layers"],
  "properties": {
    "schema": {
      "type": "string",
      "description": "Record-schema identifier; bump means a field-layout change, documented in RELEASE.json."
    },
    "release": {
      "type": "string",
      "description": "Pointer to the delivery-wide RELEASE.json (set name, release date, build timestamp)."
    },
    "clip": {
      "type": "object",
      "required": ["clip_id", "source_url", "title"],
      "properties": {
        "clip_id": { "type": "string", "minLength": 1 },
        "video_id": { "type": "string", "description": "Source platform id, kept for provenance." },
        "source_url": { "type": "string" },
        "channel": { "type": "string" },
        "channel_id": { "type": "string" },
        "title": { "type": "string" },
        "upload_date": { "type": "string", "description": "YYYYMMDD as published by the source." }
      }
    },
    "media": {
      "type": "object",
      "required": ["duration_s", "num_frames", "fps", "width", "height"],
      "properties": {
        "duration_s": { "type": "number", "exclusiveMinimum": 0 },
        "num_frames": { "type": "integer", "exclusiveMinimum": 0 },
        "fps": { "type": "number", "description": "Native frame rate; fractional rates (29.97…) are preserved exactly." },
        "width": { "type": "integer" },
        "height": { "type": "integer" },
        "resolution": { "type": "string" }
      }
    },
    "content": {
      "type": "object",
      "description": "Curation labels for this clip.",
      "properties": {
        "style_genre": { "type": "string", "description": "Movement style, e.g. breaking_bboying, taijiquan, garba." },
        "action_label": { "type": "string", "description": "Free-text action description." },
        "culture_region": { "type": "string", "description": "Culture-region bucket used for set balancing." },
        "num_persons": { "type": "integer", "minimum": 1 },
        "camera_motion": { "type": "string" },
        "motion_strength": { "type": "number", "description": "Mean normalised inter-frame keypoint displacement of the main person." },
        "truncation_frac": { "type": "number", "minimum": 0, "maximum": 1 },
        "fullbody_frac": { "type": "number", "minimum": 0, "maximum": 1 },
        "occlusion_flags": { "type": "array", "items": { "type": "string" } }
      }
    },
    "rights": {
      "type": "object",
      "required": ["license", "attribution", "rights_status"],
      "properties": {
        "license": { "type": "string", "description": "Licence of the source video, verified byte-for-byte at download time." },
        "attribution": { "type": "string", "description": "Ready-to-paste credit line; reproducing it is a licence condition when frames are redistributed." },
        "rights_status": { "type": "string" },
        "fetched_at": { "type": "string", "format": "date-time" }
      }
    },
    "files": {
      "type": "object",
      "description": "Every file of this clip with its sha256 (the record's own hash lives in the delivery manifest instead). `pose2d`/`body3d` are npz sidecars; `overlay` is the rendered proof video.",
      "required": ["video", "record", "pose2d", "body3d"],
      "additionalProperties": {
        "type": "object",
        "required": ["name"],
        "properties": {
          "name": { "type": "string" },
          "sha256": { "type": ["string", "null"], "pattern": "^[0-9a-f]{64}$" }
        }
      }
    },
    "quality": {
      "type": "object",
      "description": "Per-clip quality verdicts. Hand keypoints carry a measured reliability flag: `residual` is the estimated share of fabricated points among flag-confirmed hands, with its 95% CI — measured on blind golden probes, shipped even when it misses our own bar.",
      "required": ["hands", "annotation_acceptance", "delivery_block"],
      "properties": {
        "hands": {
          "type": "object",
          "properties": {
            "conformance": { "type": "string" },
            "residual": { "type": "number" },
            "ci95": { "type": "array", "items": { "type": "number" }, "minItems": 2, "maxItems": 2 },
            "n_probes": { "type": "integer" }
          }
        },
        "annotation_acceptance": { "type": "string" },
        "shot_integrity": { "type": "string", "description": "Single-shot verdict — no hidden cuts inside the delivered clip." },
        "delivery_block": { "type": "boolean" }
      }
    },
    "layers": {
      "type": "object",
      "description": "Array names and shapes of the npz sidecars, so a reader knows what it holds before loading. pose_2d carries [T, tracks, 133, 3] whole-body keypoints (x, y, conf) — body 0..16, feet 17..22, face 23..90, left hand 91..111, right hand 112..132. body_3d carries per-frame 3D joints, body-model parameters and camera. A separate handqa sidecar carries the per-frame, per-hand reliability flag ([T, tracks, 2]: 1 the flag did not fire, 0 it did, 2 nothing to measure) whose residual is reported in `quality.hands`. Note on the third keypoint channel: it is the pose model's native peak amplitude, not a probability — thresholding it is not a usable visibility gate; use the per-frame validity array instead.",
      "required": ["pose_2d", "body_3d"],
      "properties": {
        "pose_2d": { "type": "object", "additionalProperties": { "type": "string" } },
        "body_3d": { "type": "object", "additionalProperties": { "type": "string" } }
      }
    },
    "notes_md": {
      "type": "string",
      "description": "Optional curator notes about this clip, markdown."
    }
  }
}
