{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://theamateur.co.uk/schemas/dpl/dpl.edit-guide-1.schema.json",
  "title": "DPL editing guide (dpl.edit-guide/1)",
  "description": "What `dpl edit-guide <plan> [<takes>] --format json` writes: for an editor, scene by scene in story order, what each shot is for and its dramatic function, where to cut it (on a turning line or a reaction, shortly after a movement begins, or as an L or J cut when a line runs across the cut), which cutaway or insert covers which jump, the sound to lay, and the readiness findings recast as editing notes. Given a take log it names the chosen take of each shot and marks the shots with no usable take. Every member listed here is always written, or left out when null; nothing else appears.",
  "$comment": "Every note (`notes`, `scenes[].notes`) points at a readiness finding (`finding_id`, a `dpl.findings/1` id) and/or at plan shot ids (`shots`). A note of kind `missing_effect` cites the id of a finding that appears only because of the shots with no usable take (see `dpl.coverage-status/1` `edit_needs`). `chosen` takes follow the rule of `dpl coverage-status`: the latest circled take, else the latest good one. `reaction_window_ms` is the plan's house-style window, else 1000.",
  "type": "object",
  "properties": {
    "format": {
      "type": "string",
      "const": "dpl.edit-guide/1"
    },
    "work_id": {
      "type": "string"
    },
    "title": {
      "type": "string"
    },
    "plan_hash": {
      "description": "SHA-256 of the plan in its standard layout, as readiness reports it.",
      "type": "string",
      "pattern": "^[0-9a-f]{64}$"
    },
    "takes_hash": {
      "description": "SHA-256 of the take log in its standard layout; absent when no log was given.",
      "type": [
        "string",
        "null"
      ],
      "pattern": "^[0-9a-f]{64}$"
    },
    "rules_version": {
      "type": "string"
    },
    "reaction_window_ms": {
      "description": "The window after a turning line ends in which the listener must be seen: the plan's house style, else the readiness default.",
      "type": "integer",
      "minimum": 0
    },
    "summary": {
      "$ref": "#/$defs/GuideSummary"
    },
    "scenes": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/GuideScene"
      }
    },
    "notes": {
      "description": "Notes about the whole work, not one scene (its running time, its structure).",
      "type": "array",
      "items": {
        "$ref": "#/$defs/EditNote"
      }
    },
    "set_aside": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/SetAsideFinding"
      }
    },
    "warnings": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/DiagnosticItem"
      }
    }
  },
  "required": [
    "format",
    "work_id",
    "title",
    "plan_hash",
    "rules_version",
    "reaction_window_ms",
    "summary",
    "scenes",
    "notes",
    "set_aside",
    "warnings"
  ],
  "additionalProperties": false,
  "$defs": {
    "BridgeOver": {
      "type": "string",
      "enum": [
        "jump",
        "line_cross",
        "shortened_answer"
      ]
    },
    "ChosenBy": {
      "type": "string",
      "description": "How the take that stands for a shot was picked: the circled one, or else the latest good one.",
      "enum": [
        "circled",
        "last_good"
      ]
    },
    "ChosenTake": {
      "type": "object",
      "description": "The take that stands for a shot in the edit. Several circled takes: the latest. None circled: the latest good one.",
      "properties": {
        "op_id": {
          "type": "string"
        },
        "label": {
          "type": "string"
        },
        "chosen_by": {
          "$ref": "#/$defs/ChosenBy"
        },
        "take": {
          "type": [
            "integer",
            "null"
          ]
        },
        "files": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/TakeFile"
          }
        }
      },
      "required": [
        "op_id",
        "label",
        "chosen_by",
        "files"
      ],
      "additionalProperties": false
    },
    "CoveragePriority": {
      "type": "string",
      "description": "How much the edit depends on a shot: a must cannot be dropped without losing the scene's job.",
      "enum": [
        "must",
        "should",
        "nice"
      ]
    },
    "CoverageRole": {
      "type": "string",
      "description": "What a shot gives the edit.",
      "enum": [
        "establishing",
        "master",
        "two_shot",
        "single",
        "over_shoulder",
        "reaction",
        "insert",
        "cutaway",
        "pov",
        "neutral"
      ]
    },
    "CutAdvice": {
      "type": "object",
      "description": "A cut out of a shot, with the advice for making it. Text is absent when the plan's own time is all there is to say.",
      "properties": {
        "basis": {
          "$ref": "#/$defs/CutBasis"
        },
        "at_ms": {
          "type": "integer",
          "minimum": 0
        },
        "to": {
          "type": [
            "string",
            "null"
          ]
        },
        "text": {
          "type": [
            "string",
            "null"
          ]
        },
        "line": {
          "type": [
            "string",
            "null"
          ]
        },
        "window_ms": {
          "type": [
            "integer",
            "null"
          ]
        },
        "movement_at_ms": {
          "type": [
            "integer",
            "null"
          ]
        },
        "offset": {
          "anyOf": [
            {
              "$ref": "#/$defs/SoundOffset"
            },
            {
              "type": "null"
            }
          ]
        },
        "join": {
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "basis",
        "at_ms"
      ],
      "additionalProperties": false
    },
    "CutBasis": {
      "type": "string",
      "description": "Why a cut falls where the plan puts it, and what to do about it.",
      "enum": [
        "turning_line",
        "reaction",
        "inside_line",
        "movement",
        "scene_end",
        "planned"
      ]
    },
    "DiagnosticItem": {
      "type": "object",
      "description": "One diagnostic as an agent reads it: what rule, where, what is wrong, and what to change.",
      "properties": {
        "code": {
          "type": "string",
          "pattern": "^DPL-[A-Z]+(-[A-Z]+)*$"
        },
        "severity": {
          "$ref": "#/$defs/PlanSeverity"
        },
        "path": {
          "type": "string"
        },
        "message": {
          "type": "string"
        },
        "fix": {
          "type": "string",
          "pattern": "\\S"
        }
      },
      "required": [
        "code",
        "severity",
        "path",
        "message",
        "fix"
      ],
      "additionalProperties": false
    },
    "EditNote": {
      "type": "object",
      "description": "One thing to do in the edit. It points at a readiness finding (finding_id) and/or the shots it is about (shots).",
      "properties": {
        "id": {
          "type": "string",
          "pattern": "^N[0-9]+$"
        },
        "kind": {
          "$ref": "#/$defs/NoteKind"
        },
        "severity": {
          "anyOf": [
            {
              "$ref": "#/$defs/FindingSeverity"
            },
            {
              "type": "null"
            }
          ]
        },
        "scene": {
          "type": [
            "string",
            "null"
          ]
        },
        "shots": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "finding_id": {
          "type": [
            "string",
            "null"
          ],
          "pattern": "^f-[0-9a-f]{12}$"
        },
        "code": {
          "type": [
            "string",
            "null"
          ],
          "pattern": "^[A-Z]+(-[A-Z0-9]+)+$"
        },
        "text": {
          "type": "string"
        }
      },
      "required": [
        "id",
        "kind",
        "shots",
        "text"
      ],
      "additionalProperties": false
    },
    "FindingSeverity": {
      "type": "string",
      "enum": [
        "blocker",
        "major",
        "minor",
        "note"
      ]
    },
    "GuideBridge": {
      "type": "object",
      "description": "A shot that covers a cut: the cutaway, insert or neutral shot to lay over or between two others.",
      "properties": {
        "cover": {
          "type": "string"
        },
        "over": {
          "$ref": "#/$defs/BridgeOver"
        },
        "between": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "target": {
          "type": [
            "string",
            "null"
          ]
        },
        "finding_id": {
          "type": [
            "string",
            "null"
          ],
          "pattern": "^f-[0-9a-f]{12}$"
        },
        "text": {
          "type": "string"
        }
      },
      "required": [
        "cover",
        "over",
        "between",
        "text"
      ],
      "additionalProperties": false
    },
    "GuideScene": {
      "type": "object",
      "properties": {
        "scene": {
          "type": "string"
        },
        "purpose": {
          "type": "string"
        },
        "change": {
          "type": [
            "string",
            "null"
          ]
        },
        "location": {
          "type": [
            "string",
            "null"
          ]
        },
        "start_ms": {
          "type": "integer",
          "minimum": 0
        },
        "end_ms": {
          "type": "integer",
          "minimum": 0
        },
        "entry": {
          "type": [
            "string",
            "null"
          ]
        },
        "functions": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "shots": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/GuideShot"
          }
        },
        "bridges": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/GuideBridge"
          }
        },
        "sound": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/GuideSound"
          }
        },
        "notes": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/EditNote"
          }
        }
      },
      "required": [
        "scene",
        "purpose",
        "start_ms",
        "end_ms",
        "functions",
        "shots",
        "bridges",
        "sound",
        "notes"
      ],
      "additionalProperties": false
    },
    "GuideShot": {
      "type": "object",
      "properties": {
        "shot": {
          "type": "string"
        },
        "start_ms": {
          "type": "integer",
          "minimum": 0
        },
        "end_ms": {
          "type": "integer",
          "minimum": 0
        },
        "duration_ms": {
          "type": "integer",
          "minimum": 0
        },
        "purpose": {
          "type": "string"
        },
        "subject": {
          "type": "string"
        },
        "size": {
          "$ref": "#/$defs/ShotSize"
        },
        "role": {
          "anyOf": [
            {
              "$ref": "#/$defs/CoverageRole"
            },
            {
              "type": "null"
            }
          ]
        },
        "priority": {
          "$ref": "#/$defs/CoveragePriority"
        },
        "function": {
          "type": [
            "string",
            "null"
          ]
        },
        "setup": {
          "type": [
            "string",
            "null"
          ]
        },
        "cut": {
          "anyOf": [
            {
              "$ref": "#/$defs/CutAdvice"
            },
            {
              "type": "null"
            }
          ]
        },
        "take": {
          "anyOf": [
            {
              "$ref": "#/$defs/GuideTake"
            },
            {
              "type": "null"
            }
          ]
        },
        "notes": {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      },
      "required": [
        "shot",
        "start_ms",
        "end_ms",
        "duration_ms",
        "purpose",
        "subject",
        "size",
        "priority",
        "notes"
      ],
      "additionalProperties": false
    },
    "GuideSound": {
      "type": "object",
      "properties": {
        "kind": {
          "$ref": "#/$defs/GuideSoundKind"
        },
        "track": {
          "anyOf": [
            {
              "$ref": "#/$defs/TrackRole"
            },
            {
              "type": "null"
            }
          ]
        },
        "start_ms": {
          "type": "integer",
          "minimum": 0
        },
        "end_ms": {
          "type": "integer",
          "minimum": 0
        },
        "text": {
          "type": "string"
        },
        "source": {
          "type": [
            "string",
            "null"
          ]
        },
        "line": {
          "type": [
            "string",
            "null"
          ]
        },
        "shot": {
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "kind",
        "start_ms",
        "end_ms",
        "text"
      ],
      "additionalProperties": false
    },
    "GuideSoundKind": {
      "type": "string",
      "enum": [
        "dialogue",
        "narration",
        "room_tone",
        "sound_bridge",
        "quiet",
        "music",
        "ambience",
        "effect"
      ]
    },
    "GuideSummary": {
      "type": "object",
      "description": "The counts. The take counts are present only when a take log was given.",
      "properties": {
        "scenes": {
          "type": "integer",
          "minimum": 0
        },
        "shots": {
          "type": "integer",
          "minimum": 0
        },
        "notes": {
          "type": "integer",
          "minimum": 0
        },
        "bridges": {
          "type": "integer",
          "minimum": 0
        },
        "set_aside": {
          "type": "integer",
          "minimum": 0
        },
        "chosen_takes": {
          "type": [
            "integer",
            "null"
          ],
          "minimum": 0
        },
        "shots_without_take": {
          "type": [
            "integer",
            "null"
          ],
          "minimum": 0
        }
      },
      "required": [
        "scenes",
        "shots",
        "notes",
        "bridges",
        "set_aside"
      ],
      "additionalProperties": false
    },
    "GuideTake": {
      "type": "object",
      "description": "Where a shot stands in the take log: its chosen take, or why it has none. Usable is false for a shot with no take that counts.",
      "properties": {
        "state": {
          "$ref": "#/$defs/GuideTakeState"
        },
        "usable": {
          "type": "boolean"
        },
        "chosen": {
          "anyOf": [
            {
              "$ref": "#/$defs/ChosenTake"
            },
            {
              "type": "null"
            }
          ]
        },
        "attempts": {
          "type": "integer",
          "minimum": 0
        }
      },
      "required": [
        "state",
        "usable",
        "attempts"
      ],
      "additionalProperties": false
    },
    "GuideTakeState": {
      "type": "string",
      "enum": [
        "chosen",
        "on_hold",
        "no_good",
        "never_attempted",
        "not_filmed"
      ]
    },
    "NoteKind": {
      "type": "string",
      "enum": [
        "finding",
        "accepted",
        "missing_take",
        "missing_effect"
      ]
    },
    "PlanSeverity": {
      "type": "string",
      "enum": [
        "error",
        "warning"
      ]
    },
    "SetAsideFinding": {
      "type": "object",
      "description": "A readiness finding that is about the shoot (kit, people, light, the day), not the edit, and so is not turned into a note.",
      "properties": {
        "finding_id": {
          "type": "string",
          "pattern": "^f-[0-9a-f]{12}$"
        },
        "code": {
          "type": "string"
        },
        "scene": {
          "type": [
            "string",
            "null"
          ]
        },
        "reason": {
          "type": "string"
        }
      },
      "required": [
        "finding_id",
        "code",
        "reason"
      ],
      "additionalProperties": false
    },
    "ShotSize": {
      "type": "string",
      "enum": [
        "extreme_wide",
        "wide",
        "full",
        "medium_wide",
        "cowboy",
        "medium",
        "medium_close_up",
        "close_up",
        "extreme_close_up",
        "detail",
        "macro"
      ]
    },
    "SoundOffset": {
      "type": "string",
      "description": "Which side's sound runs across a cut: an L cut keeps the outgoing sound over the new picture, a J cut brings the incoming sound in under the old picture.",
      "enum": [
        "l_cut",
        "j_cut"
      ]
    },
    "TakeFile": {
      "type": "object",
      "description": "One camera file a take made. The name is the file's own name, so the person's editing software can relink to it.",
      "properties": {
        "camera": {
          "description": "The camera that wrote the file (a letter).",
          "type": "string"
        },
        "name": {
          "description": "The file name only, such as A001C002_261018.mov.",
          "type": "string"
        },
        "tc_in": {
          "description": "Timecode at the start of the take in this file, HH:MM:SS:FF (HH:MM:SS;FF for drop frame).",
          "type": [
            "string",
            "null"
          ]
        },
        "tc_out": {
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "camera",
        "name"
      ],
      "additionalProperties": false
    },
    "TrackRole": {
      "type": "string",
      "description": "What a track carries: the picture, the sound recorded with the picture, the narration, or room tone.",
      "enum": [
        "picture",
        "sync_sound",
        "narration",
        "room_tone"
      ]
    }
  }
}
