{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://animelegends.ai/schema/episode-packet.v1.json",
  "title": "EpisodePacket.v1",
  "description": "The canon/production graph for one AnimeLegends episode. A packet without a release record is a script, never an episode.",
  "type": "object",
  "required": [
    "schema",
    "packetId",
    "title",
    "channel",
    "characters",
    "forms",
    "abilities",
    "limitations",
    "relationships",
    "factions",
    "locations",
    "timeline",
    "arcs",
    "scenes",
    "shots",
    "assets",
    "prompts",
    "models",
    "rights",
    "sources",
    "approvals",
    "rules"
  ],
  "additionalProperties": false,
  "properties": {
    "schema": { "const": "EpisodePacket.v1" },
    "packetId": { "$ref": "#/$defs/id" },
    "title": { "type": "string", "minLength": 1 },
    "channel": { "type": "string", "minLength": 1 },
    "release": { "$ref": "#/$defs/release" },
    "characters": { "type": "array", "items": { "$ref": "#/$defs/character" } },
    "forms": { "type": "array", "items": { "$ref": "#/$defs/node" } },
    "abilities": { "type": "array", "items": { "$ref": "#/$defs/node" } },
    "limitations": { "type": "array", "items": { "$ref": "#/$defs/node" } },
    "relationships": { "type": "array", "items": { "$ref": "#/$defs/node" } },
    "factions": { "type": "array", "items": { "$ref": "#/$defs/node" } },
    "locations": { "type": "array", "items": { "$ref": "#/$defs/node" } },
    "timeline": { "type": "array", "items": { "$ref": "#/$defs/timelineEvent" } },
    "arcs": { "type": "array", "items": { "$ref": "#/$defs/node" } },
    "scenes": { "type": "array", "items": { "$ref": "#/$defs/scene" } },
    "shots": { "type": "array", "items": { "$ref": "#/$defs/shot" } },
    "assets": { "type": "array", "items": { "$ref": "#/$defs/asset" } },
    "prompts": { "type": "array", "items": { "$ref": "#/$defs/prompt" } },
    "models": { "type": "array", "items": { "$ref": "#/$defs/node" } },
    "rights": { "type": "array", "items": { "$ref": "#/$defs/rightsState" } },
    "sources": { "type": "array", "items": { "$ref": "#/$defs/source" } },
    "approvals": { "type": "array", "items": { "$ref": "#/$defs/approval" } },
    "rules": { "type": "array", "items": { "$ref": "#/$defs/continuityRule" } }
  },
  "$defs": {
    "id": { "type": "string", "pattern": "^[a-z][a-z0-9_]*$" },
    "isoDate": { "type": "string", "format": "date-time" },
    "provenance": {
      "type": "object",
      "required": ["origin", "agent", "at"],
      "properties": {
        "origin": {
          "enum": ["authored", "generated", "commissioned", "derived", "imported"]
        },
        "agent": { "type": "string", "minLength": 1 },
        "at": { "$ref": "#/$defs/isoDate" },
        "derivedFrom": { "type": "array", "items": { "$ref": "#/$defs/id" } }
      }
    },
    "node": {
      "type": "object",
      "required": ["id", "kind", "owner", "provenance", "version", "visibility", "evaluation"],
      "properties": {
        "id": { "$ref": "#/$defs/id" },
        "kind": {
          "enum": [
            "Character", "Form", "Ability", "Limitation", "Relationship", "Faction",
            "Location", "TimelineEvent", "Arc", "Scene", "Shot", "Asset", "Prompt",
            "Model", "RightsState", "Source", "Approval", "Release", "ContinuityRule"
          ]
        },
        "owner": { "type": "string", "minLength": 1 },
        "provenance": { "$ref": "#/$defs/provenance" },
        "version": { "type": "integer", "minimum": 1 },
        "visibility": { "enum": ["public", "member", "internal", "restricted"] },
        "evaluation": { "type": "array", "items": { "type": "string" } }
      }
    },
    "identityLock": {
      "type": "object",
      "required": [
        "characterId", "lockVersion", "requiredTokens", "forbiddenTokens",
        "palette", "referenceAssetIds", "matchThreshold", "imitationCeiling"
      ],
      "properties": {
        "characterId": { "$ref": "#/$defs/id" },
        "lockVersion": { "type": "integer", "minimum": 1 },
        "requiredTokens": { "type": "array", "items": { "type": "string" }, "minItems": 1 },
        "forbiddenTokens": { "type": "array", "items": { "type": "string" } },
        "palette": { "type": "array", "items": { "type": "string" } },
        "referenceAssetIds": { "type": "array", "items": { "$ref": "#/$defs/id" } },
        "matchThreshold": { "type": "number", "minimum": 0, "maximum": 1 },
        "imitationCeiling": { "type": "number", "minimum": 0, "maximum": 1 }
      }
    },
    "character": {
      "allOf": [
        { "$ref": "#/$defs/node" },
        {
          "type": "object",
          "required": ["name", "title", "archetype", "identityLock", "factionIds", "formIds"],
          "properties": { "identityLock": { "$ref": "#/$defs/identityLock" } }
        }
      ]
    },
    "timelineEvent": {
      "allOf": [
        { "$ref": "#/$defs/node" },
        {
          "type": "object",
          "required": ["name", "order", "summary"],
          "properties": { "order": { "type": "number" } }
        }
      ]
    },
    "scene": {
      "allOf": [
        { "$ref": "#/$defs/node" },
        {
          "type": "object",
          "required": [
            "arcId", "title", "atEventId", "locationId", "characterIds",
            "usesAbilityIds", "usesRelationshipIds", "conditions", "shotIds"
          ]
        }
      ]
    },
    "shot": {
      "allOf": [
        { "$ref": "#/$defs/node" },
        {
          "type": "object",
          "required": [
            "sceneId", "order", "speaker", "depictsCharacterIds", "locationId",
            "durationS", "onScreenText", "onScreenTextTiming", "voText", "assetId"
          ],
          "properties": {
            "speaker": { "enum": ["mira", "kage", "akashi", "none"] },
            "relocationEventId": { "$ref": "#/$defs/id" },
            "durationS": { "type": "number", "exclusiveMinimum": 0 },
            "onScreenTextTiming": { "enum": ["immediate", "fade_in_1s", "fade_in_2s"] }
          }
        }
      ]
    },
    "asset": {
      "allOf": [
        { "$ref": "#/$defs/node" },
        {
          "type": "object",
          "required": ["medium", "path", "sourceId", "rightsStateId", "contentHash"],
          "properties": {
            "medium": { "enum": ["image", "video", "audio", "music"] },
            "contentHash": { "type": "string", "pattern": "^[0-9a-f]{64}$" }
          }
        }
      ]
    },
    "prompt": {
      "allOf": [
        { "$ref": "#/$defs/node" },
        {
          "type": "object",
          "required": ["text", "modelId", "textHash"],
          "properties": {
            "textHash": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
            "identityLockRefs": {
              "type": "array",
              "items": {
                "type": "object",
                "required": ["characterId", "lockVersion"],
                "properties": {
                  "characterId": { "$ref": "#/$defs/id" },
                  "lockVersion": { "type": "integer", "minimum": 1 }
                }
              }
            }
          }
        }
      ]
    },
    "rightsState": {
      "allOf": [
        { "$ref": "#/$defs/node" },
        {
          "type": "object",
          "required": ["status", "evidence", "usage", "territories"],
          "properties": {
            "status": { "enum": ["owned", "commissioned", "licensed", "unknown", "restricted"] },
            "evidence": {
              "type": "array",
              "items": {
                "type": "object",
                "required": ["kind", "ref", "hash", "verifiedAt"],
                "properties": {
                  "kind": {
                    "enum": [
                      "assignment", "contract", "licence", "invoice",
                      "model-terms", "release-form"
                    ]
                  },
                  "ref": { "type": "string", "minLength": 1 },
                  "hash": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
                  "verifiedAt": { "$ref": "#/$defs/isoDate" },
                  "validUntil": { "$ref": "#/$defs/isoDate" }
                }
              }
            }
          }
        }
      ]
    },
    "source": {
      "allOf": [
        { "$ref": "#/$defs/node" },
        {
          "type": "object",
          "required": ["sourceKind", "description"],
          "properties": {
            "sourceKind": { "enum": ["original", "commissioned", "third-party", "reference"] }
          }
        }
      ]
    },
    "approval": {
      "allOf": [
        { "$ref": "#/$defs/node" },
        {
          "type": "object",
          "required": ["subjectRef", "reviewer", "decision", "scope", "at", "notes"],
          "properties": {
            "decision": { "enum": ["approved", "changes-requested", "rejected"] },
            "scope": { "enum": ["continuity", "rights", "craft", "packet"] },
            "packetHash": { "type": "string", "pattern": "^[0-9a-f]{64}$" }
          }
        }
      ]
    },
    "release": {
      "allOf": [
        { "$ref": "#/$defs/node" },
        {
          "type": "object",
          "required": [
            "packetId", "episodeNumber", "title", "channel", "releasedAt", "ledgerHash"
          ],
          "properties": {
            "episodeNumber": { "type": "integer", "minimum": 1 },
            "ledgerHash": { "type": "string", "pattern": "^[0-9a-f]{64}$" }
          }
        }
      ]
    },
    "continuityRule": {
      "allOf": [
        { "$ref": "#/$defs/node" },
        {
          "type": "object",
          "required": ["ruleId", "description", "severity"],
          "properties": { "severity": { "enum": ["blocker", "warning"] } }
        }
      ]
    }
  }
}
