{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://theamateur.co.uk/schemas/dpl/dpl.coverage-status-1.schema.json",
  "title": "DPL coverage status (dpl.coverage-status/1)",
  "description": "What `dpl coverage-status <plan> <takes> --format json` writes: \"did we get it?\", asked on set before leaving a location. For the location, day or scene in scope: the shots with no good or circled take (must-haves first), the shots never attempted, what the edit will lack because of them (edit-readiness findings that appear when the rules are run on the plan with only the shots that have a usable take), what the next setup must match, and whether room tone was recorded. Every member listed here is always written, or left out when null; nothing else appears.",
  "$comment": "The command exits 0 when `summary.complete` (every must-have shot in scope has a usable take) and 5 otherwise. A usable take is an effective (not superseded) take of kind `full` or `pickup` whose verdict is `good` or `circled`. `edit_needs` use finding ids and codes of `dpl.findings/1`. `warnings` are `dpl.diagnostics/1` items about the take log.",
  "type": "object",
  "properties": {
    "format": {
      "type": "string",
      "const": "dpl.coverage-status/1"
    },
    "work_id": {
      "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.",
      "type": "string",
      "pattern": "^[0-9a-f]{64}$"
    },
    "rules_version": {
      "description": "The readiness rules the edit needs were worked out with.",
      "type": "string"
    },
    "scope": {
      "$ref": "#/$defs/ScopeInfo"
    },
    "summary": {
      "$ref": "#/$defs/CoverageSummary"
    },
    "shots": {
      "description": "Every shot in scope that needs a take, in plan order, with where it stands.",
      "type": "array",
      "items": {
        "$ref": "#/$defs/ShotStatus"
      }
    },
    "missing": {
      "description": "The shots with no usable take: must-haves first, then should-haves, then nice-to-haves, each in plan order.",
      "type": "array",
      "items": {
        "$ref": "#/$defs/MissingShot"
      }
    },
    "never_attempted": {
      "description": "The ids of the shots in scope with no take that counts as an attempt (any priority).",
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "edit_needs": {
      "description": "What the edit will lack because of what is still missing, worst first.",
      "type": "array",
      "items": {
        "$ref": "#/$defs/EditNeed"
      }
    },
    "continuity": {
      "$ref": "#/$defs/ContinuityStatus"
    },
    "room_tone": {
      "description": "Room tone, where the plan or its sound notes expect it.",
      "type": "array",
      "items": {
        "$ref": "#/$defs/RoomToneStatus"
      }
    },
    "takes": {
      "$ref": "#/$defs/TakeCounts"
    },
    "warnings": {
      "description": "Warnings found reading the take log and the plan, each with a fix.",
      "type": "array",
      "items": {
        "$ref": "#/$defs/DiagnosticItem"
      }
    }
  },
  "required": [
    "format",
    "work_id",
    "plan_hash",
    "takes_hash",
    "rules_version",
    "scope",
    "summary",
    "shots",
    "missing",
    "never_attempted",
    "edit_needs",
    "continuity",
    "room_tone",
    "takes",
    "warnings"
  ],
  "additionalProperties": false,
  "$defs": {
    "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
    },
    "ContinuityStatus": {
      "type": "object",
      "description": "The next setup to shoot and what it must match; every setup that still has shots to get.",
      "properties": {
        "next_setup": {
          "anyOf": [
            {
              "$ref": "#/$defs/PendingSetup"
            },
            {
              "type": "null"
            }
          ]
        },
        "match": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/MatchItem"
          }
        },
        "pending": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/PendingSetup"
          }
        }
      },
      "required": [
        "match",
        "pending"
      ],
      "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"
      ]
    },
    "CoverageSummary": {
      "type": "object",
      "description": "The counts. complete is true when every must-have shot in scope has a usable take; it is what the exit code is.",
      "properties": {
        "shots": {
          "type": "integer",
          "minimum": 0
        },
        "covered": {
          "type": "integer",
          "minimum": 0
        },
        "must_total": {
          "type": "integer",
          "minimum": 0
        },
        "must_missing": {
          "type": "integer",
          "minimum": 0
        },
        "should_total": {
          "type": "integer",
          "minimum": 0
        },
        "should_missing": {
          "type": "integer",
          "minimum": 0
        },
        "nice_total": {
          "type": "integer",
          "minimum": 0
        },
        "nice_missing": {
          "type": "integer",
          "minimum": 0
        },
        "never_attempted": {
          "type": "integer",
          "minimum": 0
        },
        "on_hold": {
          "type": "integer",
          "minimum": 0
        },
        "edit_needs": {
          "type": "integer",
          "minimum": 0
        },
        "room_tone_missing": {
          "type": "integer",
          "minimum": 0
        },
        "complete": {
          "type": "boolean"
        }
      },
      "required": [
        "shots",
        "covered",
        "must_total",
        "must_missing",
        "should_total",
        "should_missing",
        "nice_total",
        "nice_missing",
        "never_attempted",
        "on_hold",
        "edit_needs",
        "room_tone_missing",
        "complete"
      ],
      "additionalProperties": false
    },
    "DaysFrom": {
      "type": "string",
      "description": "Where --day got its meaning: the plan's own shoot schedule, or (for a plan with none) the dates the takes were logged.",
      "enum": [
        "schedule",
        "logged_at"
      ]
    },
    "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
    },
    "EditNeed": {
      "type": "object",
      "description": "Something the edit will lack. It is a readiness finding (dpl.findings/1 id and code) that appears when the rules are run on the plan with only the shots that have a usable take, and did not appear on the full plan. caused_by names the missing shots whose absence causes it: the ones that, taken alone, would make it go away; when no single shot does, all the missing shots of its scene.",
      "properties": {
        "finding_id": {
          "type": "string",
          "pattern": "^f-[0-9a-f]{12}$"
        },
        "code": {
          "type": "string",
          "pattern": "^RDY-[A-Z0-9-]+$"
        },
        "severity": {
          "$ref": "#/$defs/FindingSeverity"
        },
        "scene": {
          "type": "string"
        },
        "shot": {
          "type": [
            "string",
            "null"
          ]
        },
        "effect": {
          "$ref": "#/$defs/EditNeedEffect"
        },
        "message": {
          "type": "string"
        },
        "caused_by": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "needs_several": {
          "type": "boolean"
        },
        "fix": {
          "type": "string"
        }
      },
      "required": [
        "finding_id",
        "code",
        "severity",
        "scene",
        "effect",
        "message",
        "caused_by",
        "needs_several",
        "fix"
      ],
      "additionalProperties": false
    },
    "EditNeedEffect": {
      "type": "string",
      "description": "Whether a finding is new because of what is missing, or an existing finding that gets worse.",
      "enum": [
        "new",
        "worse"
      ]
    },
    "FindingSeverity": {
      "type": "string",
      "enum": [
        "blocker",
        "major",
        "minor",
        "note"
      ]
    },
    "MatchItem": {
      "type": "object",
      "description": "One thing the next setup has to match, from the plan's prop states, state changes, continuity rules and staging.",
      "properties": {
        "kind": {
          "$ref": "#/$defs/MatchKind"
        },
        "shot": {
          "type": "string"
        },
        "subject": {
          "type": [
            "string",
            "null"
          ]
        },
        "state": {
          "type": [
            "string",
            "null"
          ]
        },
        "note": {
          "type": "string"
        },
        "reference": {
          "anyOf": [
            {
              "$ref": "#/$defs/ReferenceTake"
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "kind",
        "shot",
        "note"
      ],
      "additionalProperties": false
    },
    "MatchKind": {
      "type": "string",
      "description": "What kind of thing the next setup has to match.",
      "enum": [
        "prop_state",
        "state_change",
        "continuity_rule",
        "visual_state",
        "screen_position"
      ]
    },
    "MissingShot": {
      "type": "object",
      "description": "A shot in scope with no usable take, and the next step.",
      "properties": {
        "shot": {
          "type": "string"
        },
        "scene": {
          "type": "string"
        },
        "location": {
          "type": [
            "string",
            "null"
          ]
        },
        "priority": {
          "$ref": "#/$defs/CoveragePriority"
        },
        "setup": {
          "type": [
            "string",
            "null"
          ]
        },
        "purpose": {
          "type": "string"
        },
        "state": {
          "$ref": "#/$defs/ShotState"
        },
        "attempts": {
          "type": "integer",
          "minimum": 0
        },
        "fix": {
          "type": "string"
        }
      },
      "required": [
        "shot",
        "scene",
        "priority",
        "purpose",
        "state",
        "attempts",
        "fix"
      ],
      "additionalProperties": false
    },
    "PendingSetup": {
      "type": "object",
      "description": "A setup with shots still to get, in the scene it is in.",
      "properties": {
        "setup": {
          "type": [
            "string",
            "null"
          ]
        },
        "scene": {
          "type": "string"
        },
        "shots": {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      },
      "required": [
        "scene",
        "shots"
      ],
      "additionalProperties": false
    },
    "PlanSeverity": {
      "type": "string",
      "enum": [
        "error",
        "warning"
      ]
    },
    "ReferenceTake": {
      "type": "object",
      "description": "A take of a shot already shot, to match against.",
      "properties": {
        "shot": {
          "type": "string"
        },
        "op_id": {
          "type": "string"
        },
        "label": {
          "type": "string"
        },
        "files": {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      },
      "required": [
        "shot",
        "op_id",
        "label",
        "files"
      ],
      "additionalProperties": false
    },
    "RoomToneStatus": {
      "type": "object",
      "description": "Whether room tone is expected at a place, why, and whether a room_tone take was logged for it.",
      "properties": {
        "location": {
          "type": [
            "string",
            "null"
          ]
        },
        "scenes": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "reasons": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "recorded": {
          "type": "boolean"
        },
        "takes": {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      },
      "required": [
        "scenes",
        "reasons",
        "recorded",
        "takes"
      ],
      "additionalProperties": false
    },
    "ScopeInfo": {
      "type": "object",
      "description": "The scope as resolved: what was asked, and the scenes that met it. DaysFrom is present when days were asked for.",
      "properties": {
        "locations": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "scenes": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "days": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "next_setup": {
          "type": [
            "string",
            "null"
          ]
        },
        "scenes_in_scope": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "days_from": {
          "anyOf": [
            {
              "$ref": "#/$defs/DaysFrom"
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "locations",
        "scenes",
        "days",
        "scenes_in_scope"
      ],
      "additionalProperties": false
    },
    "ShotState": {
      "type": "string",
      "description": "Where the shot stands: a usable take (good or circled), takes on hold, takes that are all no good, or nothing tried.",
      "enum": [
        "covered",
        "on_hold",
        "no_good",
        "never_attempted"
      ]
    },
    "ShotStatus": {
      "type": "object",
      "description": "One shot in scope and where it stands.",
      "properties": {
        "shot": {
          "type": "string"
        },
        "scene": {
          "type": "string"
        },
        "location": {
          "type": [
            "string",
            "null"
          ]
        },
        "priority": {
          "$ref": "#/$defs/CoveragePriority"
        },
        "setup": {
          "type": [
            "string",
            "null"
          ]
        },
        "purpose": {
          "type": "string"
        },
        "state": {
          "$ref": "#/$defs/ShotState"
        },
        "attempts": {
          "type": "integer",
          "minimum": 0
        },
        "chosen": {
          "anyOf": [
            {
              "$ref": "#/$defs/ChosenTake"
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "shot",
        "scene",
        "priority",
        "purpose",
        "state",
        "attempts"
      ],
      "additionalProperties": false
    },
    "TakeCounts": {
      "type": "object",
      "description": "What the take log held: entries, those that count, corrected ones and folded repeats.",
      "properties": {
        "logged": {
          "type": "integer",
          "minimum": 0
        },
        "effective": {
          "type": "integer",
          "minimum": 0
        },
        "superseded": {
          "type": "integer",
          "minimum": 0
        },
        "duplicates_collapsed": {
          "type": "integer",
          "minimum": 0
        }
      },
      "required": [
        "logged",
        "effective",
        "superseded",
        "duplicates_collapsed"
      ],
      "additionalProperties": false
    },
    "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
    }
  }
}
