{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://rizzgen.ai/schema/take-1.0.schema.json",
  "title": "take/1.0",
  "description": "A version graph for a generated video project: shots, branches, and every take that was made, including the ones that were thrown away.",
  "type": "object",
  "required": ["format", "project", "shots", "branches", "takes", "timeline"],
  "additionalProperties": true,
  "properties": {
    "format": {
      "type": "string",
      "pattern": "^take/1\\.[0-9]+$",
      "description": "Format identifier and version. Readers must reject unknown major versions and accept unknown minor versions."
    },
    "project": { "$ref": "#/$defs/project" },
    "assets": { "type": "array", "items": { "$ref": "#/$defs/asset" }, "default": [] },
    "shots": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/shot" } },
    "branches": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/branch" } },
    "takes": { "type": "array", "items": { "$ref": "#/$defs/take" } },
    "timeline": { "$ref": "#/$defs/timeline" }
  },

  "$defs": {

    "takeId": {
      "type": "string",
      "pattern": "^t_[0-9abcdefghjkmnpqrstvwxyz]{8}$",
      "description": "'t_' followed by 8 lowercase Crockford Base32 characters (i, l, o, u excluded). Unique within the document."
    },
    "assetId": {
      "type": "string",
      "pattern": "^a_[0-9abcdefghjkmnpqrstvwxyz]{4,16}$"
    },
    "shotId": {
      "type": "string",
      "pattern": "^[a-z0-9][a-z0-9_-]{0,63}$"
    },
    "branchName": {
      "type": "string",
      "pattern": "^[a-z0-9][a-z0-9._-]*(/[a-z0-9][a-z0-9._-]*)*$",
      "maxLength": 64,
      "description": "Git-style branch name. No leading, trailing or consecutive slashes."
    },
    "projectId": {
      "type": "string",
      "pattern": "^[0-9a-f]{4}-[a-z0-9]([a-z0-9-]{0,78}[a-z0-9])?$",
      "description": "Four hex characters, a hyphen, then a kebab-case slug. Used as the public URL path segment."
    },
    "sha256": {
      "type": "string",
      "pattern": "^[0-9a-f]{64}$"
    },
    "uri": {
      "type": "string",
      "pattern": "^(cas://sha256/[0-9a-f]{64}|https://.+)$",
      "description": "Content-addressed reference, or an HTTPS URL. Readers must not dereference URIs from untrusted documents without an allowlist."
    },
    "timestamp": {
      "type": "string",
      "format": "date-time",
      "description": "RFC 3339 timestamp in UTC, with a trailing Z."
    },

    "rejectReason": {
      "type": "string",
      "enum": [
        "identity_drift",
        "camera_disobeyed",
        "text_garbled",
        "continuity_break",
        "prop_inconsistency",
        "color_mismatch",
        "anatomy",
        "artifact",
        "prompt_miss",
        "pacing",
        "note"
      ]
    },

    "project": {
      "type": "object",
      "required": ["id", "title", "created_at", "default_branch"],
      "additionalProperties": true,
      "properties": {
        "id": { "$ref": "#/$defs/projectId" },
        "title": { "type": "string", "minLength": 1, "maxLength": 200 },
        "summary": { "type": ["string", "null"], "maxLength": 500 },
        "created_at": { "$ref": "#/$defs/timestamp" },
        "default_branch": { "$ref": "#/$defs/branchName" },
        "aspect": { "type": ["string", "null"], "examples": ["16:9", "9:16", "1:1"] },
        "forked_from": {
          "oneOf": [
            { "type": "null" },
            {
              "type": "object",
              "required": ["project", "take"],
              "properties": {
                "project": { "$ref": "#/$defs/projectId" },
                "take": { "$ref": "#/$defs/takeId" },
                "url": { "type": ["string", "null"] }
              },
              "additionalProperties": true
            }
          ]
        }
      }
    },

    "asset": {
      "type": "object",
      "required": ["id", "kind", "name"],
      "additionalProperties": true,
      "properties": {
        "id": { "$ref": "#/$defs/assetId" },
        "kind": {
          "type": "string",
          "enum": ["character", "prop", "style", "plate", "audio"]
        },
        "name": { "type": "string", "minLength": 1, "maxLength": 200 },
        "uri": { "oneOf": [{ "$ref": "#/$defs/uri" }, { "type": "null" }] },
        "sha256": { "oneOf": [{ "$ref": "#/$defs/sha256" }, { "type": "null" }] }
      }
    },

    "shot": {
      "type": "object",
      "required": ["id", "index", "name"],
      "additionalProperties": true,
      "properties": {
        "id": { "$ref": "#/$defs/shotId" },
        "index": { "type": "integer", "minimum": 0 },
        "name": { "type": "string", "minLength": 1, "maxLength": 200 },
        "brief": { "type": ["string", "null"], "maxLength": 2000 },
        "duration_s": { "type": ["number", "null"], "exclusiveMinimum": 0 },
        "aspect": { "type": ["string", "null"] }
      }
    },

    "branch": {
      "type": "object",
      "required": ["name", "base_take"],
      "additionalProperties": true,
      "properties": {
        "name": { "$ref": "#/$defs/branchName" },
        "base_take": {
          "oneOf": [{ "$ref": "#/$defs/takeId" }, { "type": "null" }],
          "description": "The take this branch forked from. Null only for the root branch."
        },
        "note": { "type": ["string", "null"], "maxLength": 1000 },
        "created_at": { "$ref": "#/$defs/timestamp" }
      }
    },

    "model": {
      "type": "object",
      "required": ["provider", "name", "provider_version"],
      "additionalProperties": true,
      "properties": {
        "provider": { "type": "string", "minLength": 1 },
        "name": { "type": "string", "minLength": 1 },
        "provider_version": {
          "type": ["string", "null"],
          "description": "The most specific version identifier the provider exposes. When the provider exposes none, '<name>@<YYYY-MM-DD>' using the UTC date of the call. Null only when neither is knowable, which makes the take non-replayable."
        }
      }
    },

    "ref": {
      "type": "object",
      "required": ["asset", "role"],
      "additionalProperties": true,
      "properties": {
        "asset": { "$ref": "#/$defs/assetId" },
        "role": {
          "type": "string",
          "enum": ["identity", "continuity", "prop", "style", "audio"]
        },
        "weight": { "type": ["number", "null"], "minimum": 0, "maximum": 1 }
      }
    },

    "inputs": {
      "type": "object",
      "required": ["prompt", "seed"],
      "additionalProperties": true,
      "description": "The reproducibility contract. Everything needed to re-run this generation must appear here, exactly as sent to the provider.",
      "properties": {
        "prompt": { "type": "string" },
        "negative": { "type": ["string", "null"] },
        "seed": {
          "type": ["integer", "null"],
          "description": "The seed sent to the provider. Null only when the provider does not accept one, which makes the take non-replayable."
        },
        "duration_s": { "type": ["number", "null"], "exclusiveMinimum": 0 },
        "fps": { "type": ["integer", "null"], "exclusiveMinimum": 0 },
        "resolution": { "type": ["string", "null"], "pattern": "^[0-9]+x[0-9]+$" },
        "refs": { "type": "array", "items": { "$ref": "#/$defs/ref" }, "default": [] },
        "provider_params": {
          "type": "object",
          "additionalProperties": true,
          "description": "Every remaining provider-specific parameter, verbatim."
        }
      }
    },

    "output": {
      "oneOf": [
        { "type": "null" },
        {
          "type": "object",
          "required": ["uri"],
          "additionalProperties": true,
          "properties": {
            "uri": { "$ref": "#/$defs/uri" },
            "sha256": { "oneOf": [{ "$ref": "#/$defs/sha256" }, { "type": "null" }] },
            "bytes": { "type": ["integer", "null"], "minimum": 0 },
            "duration_s": { "type": ["number", "null"], "minimum": 0 },
            "thumbnail": { "oneOf": [{ "$ref": "#/$defs/uri" }, { "type": "null" }] }
          }
        }
      ]
    },

    "verdict": {
      "type": "object",
      "required": ["status"],
      "additionalProperties": true,
      "description": "Advisory only. Readers must recompute status from the timeline and reason, and must prefer the computed value.",
      "properties": {
        "status": {
          "type": "string",
          "enum": ["keeper", "rejected", "superseded", "pending"]
        },
        "reason": {
          "oneOf": [{ "$ref": "#/$defs/rejectReason" }, { "type": "null" }]
        },
        "note": { "type": ["string", "null"], "maxLength": 1000 },
        "by": { "type": ["string", "null"], "maxLength": 100 },
        "at": { "oneOf": [{ "$ref": "#/$defs/timestamp" }, { "type": "null" }] }
      }
    },

    "cost": {
      "oneOf": [
        { "type": "null" },
        {
          "type": "object",
          "required": ["currency", "amount"],
          "additionalProperties": false,
          "properties": {
            "currency": { "type": "string", "pattern": "^[A-Z]{3}$" },
            "amount": { "type": "number", "minimum": 0 }
          }
        }
      ]
    },

    "error": {
      "oneOf": [
        { "type": "null" },
        {
          "type": "object",
          "required": ["code"],
          "additionalProperties": true,
          "properties": {
            "code": {
              "type": "string",
              "enum": ["provider_error", "safety_refusal", "timeout", "quota", "cancelled", "invalid_input"]
            },
            "message": { "type": ["string", "null"], "maxLength": 2000 }
          }
        }
      ]
    },

    "take": {
      "type": "object",
      "required": ["id", "parent", "shot", "branch", "created_at", "model", "inputs"],
      "additionalProperties": true,
      "properties": {
        "id": { "$ref": "#/$defs/takeId" },
        "parent": {
          "oneOf": [{ "$ref": "#/$defs/takeId" }, { "type": "null" }],
          "description": "The take this one was derived from. May belong to a different shot or branch. Null for a root take."
        },
        "shot": { "$ref": "#/$defs/shotId" },
        "branch": { "$ref": "#/$defs/branchName" },
        "created_at": { "$ref": "#/$defs/timestamp" },
        "status": {
          "type": "string",
          "enum": ["succeeded", "failed", "cancelled"],
          "default": "succeeded",
          "description": "Terminal status only. In-flight takes are not serialised."
        },
        "model": { "$ref": "#/$defs/model" },
        "inputs": { "$ref": "#/$defs/inputs" },
        "inputs_hash": {
          "type": ["string", "null"],
          "pattern": "^sha256:[0-9a-f]{64}$",
          "description": "sha256 of the RFC 8785 canonicalisation of inputs, prefixed 'sha256:'."
        },
        "output": { "$ref": "#/$defs/output" },
        "verdict": { "$ref": "#/$defs/verdict" },
        "cost": { "$ref": "#/$defs/cost" },
        "latency_ms": { "type": ["integer", "null"], "minimum": 0 },
        "error": { "$ref": "#/$defs/error" },
        "source_status": {
          "type": "string",
          "enum": ["succeeded", "failed", "cancelled"],
          "description": "Original outcome of an input-only fork record. The new record omits status and output because no local generation has occurred. Requires source_take."
        },
        "redactions": {
          "type": "array",
          "items": { "type": "string", "maxLength": 500 },
          "maxItems": 20,
          "description": "Plain-language list of what this record does NOT carry. Present on every take in a published document, where output media, seeds, provider parameters and reference files are withheld; absent from an owner export, which carries them."
        },
        "source_note": {
          "type": "string",
          "maxLength": 4000,
          "description": "Full original note when it exceeds the portable verdict.note display limit."
        },
        "source_take": {
          "oneOf": [{ "$ref": "#/$defs/takeId" }, { "type": "null" }],
          "description": "The id this take carried in the document it was forked from, if any."
        }
      },
      "allOf": [
        {
          "if": { "properties": { "status": { "const": "succeeded" } }, "required": ["status"] },
          "then": { "properties": { "output": { "not": { "type": "null" } } }, "required": ["output"] }
        },
        {
          "if": { "properties": { "status": { "const": "failed" } }, "required": ["status"] },
          "then": { "properties": { "error": { "not": { "type": "null" } } }, "required": ["error"] }
        }
      ]
    },

    "timeline": {
      "type": "object",
      "minProperties": 1,
      "description": "For each branch, which take occupies each shot. A take listed here is a keeper on that branch.",
      "propertyNames": { "$ref": "#/$defs/branchName" },
      "additionalProperties": {
        "type": "object",
        "propertyNames": { "$ref": "#/$defs/shotId" },
        "additionalProperties": {
          "oneOf": [
            { "$ref": "#/$defs/takeId" },
            { "type": "null" }
          ]
        }
      }
    }
  }
}
